mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-19 13:54:25 +01:00
using stripe api + improved ui + ui to configure schedules reference
TODO: as an admin, I can select if payment_method = (stripe || local)
This commit is contained in:
parent
881f8d3efb
commit
678af0de1f
@ -22,7 +22,7 @@ class API::SubscriptionsController < API::ApiController
|
||||
is_subscribe = Subscriptions::Subscribe.new(current_user.invoicing_profile.id, user_id)
|
||||
.pay_and_save(@subscription, coupon: coupon_params[:coupon_code],
|
||||
invoice: true,
|
||||
schedule: params[:subcription][:payment_schedule])
|
||||
schedule: params[:subscription][:payment_schedule])
|
||||
|
||||
if is_subscribe
|
||||
render :show, status: :created, location: @subscription
|
||||
|
@ -94,7 +94,6 @@ const PaymentScheduleSummaryWrapper: React.FC<PaymentScheduleSummaryProps> = ({
|
||||
return (
|
||||
<Loader>
|
||||
<PaymentScheduleSummary schedule={schedule} $filter={$filter} />
|
||||
<div>lorem ipsum</div>
|
||||
</Loader>
|
||||
);
|
||||
}
|
||||
|
@ -260,6 +260,8 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
||||
sample = sample.replace(/W\[([^\]]+)\]/g, '');
|
||||
// information about refunds (R[text]) - does not apply here
|
||||
sample = sample.replace(/R\[([^\]]+)\]/g, '');
|
||||
// information about payment schedules (S[text]) -does not apply here
|
||||
sample = sample.replace(/S\[([^\]]+)\]/g, '');
|
||||
}
|
||||
return sample;
|
||||
};
|
||||
|
@ -353,13 +353,13 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
|
||||
// the moment when the slot selection changed for the last time, used to trigger changes in the cart
|
||||
$scope.selectionTime = null;
|
||||
|
||||
// the last clicked event in the calender
|
||||
// the last clicked event in the calendar
|
||||
$scope.selectedEvent = null;
|
||||
|
||||
// indicates the state of the current view : calendar or plans information
|
||||
$scope.plansAreShown = false;
|
||||
|
||||
// will store the user's plan if he choosed to buy one
|
||||
// will store the user's plan if he chose to buy one
|
||||
$scope.selectedPlan = null;
|
||||
|
||||
// the moment when the plan selection changed for the last time, used to trigger changes in the cart
|
||||
@ -390,7 +390,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
|
||||
$scope.spaceExplicationsAlert = settingsPromise.space_explications_alert;
|
||||
|
||||
/**
|
||||
* Change the last selected slot's appearence to looks like 'added to cart'
|
||||
* Change the last selected slot's appearance to looks like 'added to cart'
|
||||
*/
|
||||
$scope.markSlotAsAdded = function () {
|
||||
$scope.selectedEvent.backgroundColor = SELECTED_EVENT_BG_COLOR;
|
||||
@ -398,7 +398,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the last selected slot's appearence to looks like 'never added to cart'
|
||||
* Change the last selected slot's appearance to looks like 'never added to cart'
|
||||
*/
|
||||
$scope.markSlotAsRemoved = function (slot) {
|
||||
slot.backgroundColor = 'white';
|
||||
@ -419,7 +419,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
|
||||
$scope.slotCancelled = function () { $scope.markSlotAsRemoved($scope.selectedEvent); };
|
||||
|
||||
/**
|
||||
* Change the last selected slot's appearence to looks like 'currently looking for a new destination to exchange'
|
||||
* Change the last selected slot's appearance to looks like 'currently looking for a new destination to exchange'
|
||||
*/
|
||||
$scope.markSlotAsModifying = function () {
|
||||
$scope.selectedEvent.backgroundColor = '#eee';
|
||||
@ -428,7 +428,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the last selected slot's appearence to looks like 'the slot being exchanged will take this place'
|
||||
* Change the last selected slot's appearance to looks like 'the slot being exchanged will take this place'
|
||||
*/
|
||||
$scope.changeModifySpaceSlot = function () {
|
||||
if ($scope.events.placable) {
|
||||
@ -538,7 +538,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
|
||||
};
|
||||
|
||||
/**
|
||||
* Changes the user current view from the plan subsription screen to the machine reservation agenda
|
||||
* Changes the user current view from the plan subscription screen to the machine reservation agenda
|
||||
* @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
|
||||
*/
|
||||
$scope.doNotSubscribePlan = function (e) {
|
||||
@ -612,7 +612,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
|
||||
/**
|
||||
* Triggered when the user clicks on a reservation slot in the agenda.
|
||||
* Defines the behavior to adopt depending on the slot status (already booked, free, ready to be reserved ...),
|
||||
* the user's subscription (current or about to be took) and the time (the user cannot modify a booked reservation
|
||||
* the user's subscription (current or about to be took), and the time (the user cannot modify a booked reservation
|
||||
* if it's too late).
|
||||
* @see http://fullcalendar.io/docs/mouse/eventClick/
|
||||
*/
|
||||
@ -622,7 +622,7 @@ Application.Controllers.controller('ReserveSpaceController', ['$scope', '$stateP
|
||||
};
|
||||
|
||||
/**
|
||||
* Triggered when fullCalendar tries to graphicaly render an event block.
|
||||
* Triggered when fullCalendar tries to graphically render an event block.
|
||||
* Append the event tag into the block, just after the event title.
|
||||
* @see http://fullcalendar.io/docs/event_rendering/eventRender/
|
||||
*/
|
||||
|
@ -312,24 +312,24 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
||||
// What the bound slot
|
||||
$scope.$watch('slotSelectionTime', function (newValue, oldValue) {
|
||||
if (newValue !== oldValue) {
|
||||
return slotSelectionChanged();
|
||||
slotSelectionChanged();
|
||||
}
|
||||
});
|
||||
$scope.$watch('user', function (newValue, oldValue) {
|
||||
if (newValue !== oldValue) {
|
||||
resetCartState();
|
||||
return updateCartPrice();
|
||||
updateCartPrice();
|
||||
}
|
||||
});
|
||||
$scope.$watch('planSelectionTime', function (newValue, oldValue) {
|
||||
if (newValue !== oldValue) {
|
||||
return planSelectionChanged();
|
||||
planSelectionChanged();
|
||||
}
|
||||
});
|
||||
// watch when a coupon is applied to re-compute the total price
|
||||
$scope.$watch('coupon.applied', function (newValue, oldValue) {
|
||||
if (newValue !== oldValue) {
|
||||
return updateCartPrice();
|
||||
updateCartPrice();
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -524,6 +524,7 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
||||
*/
|
||||
const resetCartState = function () {
|
||||
$scope.selectedPlan = null;
|
||||
$scope.paidPlan = null;
|
||||
$scope.coupon.applied = null;
|
||||
$scope.events.moved = null;
|
||||
$scope.events.paid = [];
|
||||
@ -838,9 +839,16 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
||||
// we call the external callback if present
|
||||
if (typeof $scope.afterPayment === 'function') { $scope.afterPayment(reservation); }
|
||||
// we reset the coupon, and the cart content, and we unselect the slot
|
||||
$scope.events.reserved = [];
|
||||
$scope.coupon.applied = null;
|
||||
$scope.slot = null;
|
||||
if ($scope.slot) {
|
||||
// reservation (+ subscription)
|
||||
$scope.slot = null;
|
||||
$scope.events.reserved = [];
|
||||
} else {
|
||||
// subscription only
|
||||
$scope.events = {};
|
||||
}
|
||||
$scope.paidPlan = $scope.selectedPlan;
|
||||
$scope.selectedPlan = null;
|
||||
$scope.schedule.requested_schedule = false;
|
||||
$scope.schedule.payment_schedule = null;
|
||||
|
@ -137,12 +137,18 @@
|
||||
|
||||
<script type="text/ng-template" id="addOnlineInfo.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>X[texte]</strong></td><td>{{ 'app.admin.invoices.add_a_notice_regarding_the_online_sales_only_if_the_invoice_is_concerned' | translate }} <mark translate>{{ 'app.admin.invoices.this_will_never_be_added_when_a_refund_notice_is_present' }}</mark> {{ 'app.admin.invoices.eg_XVL_will_add_VL_to_the_invoices_settled_with_stripe' | translate }}</td></tr>
|
||||
<tr><td><strong>X[{{ 'app.admin.invoices.text' | translate }}]</strong></td><td>{{ 'app.admin.invoices.add_a_notice_regarding_the_online_sales_only_if_the_invoice_is_concerned' | translate }} <mark translate>{{ 'app.admin.invoices.this_will_never_be_added_when_a_refund_notice_is_present' }}</mark> {{ 'app.admin.invoices.eg_XVL_will_add_VL_to_the_invoices_settled_with_stripe' | translate }}</td></tr>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addRefundInfo.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>R[texte]</strong></td><td>{{ 'app.admin.invoices.add_a_notice_regarding_refunds_only_if_the_invoice_is_concerned' | translate }}<mark translate>{{ 'app.admin.invoices.this_will_never_be_added_when_an_online_sales_notice_is_present' }}</mark> {{ 'app.admin.invoices.eg_RA_will_add_A_to_the_refund_invoices' | translate }}</td></tr>
|
||||
<tr><td><strong>R[{{ 'app.admin.invoices.text' | translate }}]</strong></td><td>{{ 'app.admin.invoices.add_a_notice_regarding_refunds_only_if_the_invoice_is_concerned' | translate }}<mark translate>{{ 'app.admin.invoices.this_will_never_be_added_when_an_online_sales_notice_is_present' }}</mark> {{ 'app.admin.invoices.eg_RA_will_add_A_to_the_refund_invoices' | translate }}</td></tr>
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="addPaymentScheduleInfo.html">
|
||||
<table class="invoice-element-legend">
|
||||
<tr><td><strong>S[{{ 'app.admin.invoices.text' | translate }}]</strong></td><td>{{ 'app.admin.invoices.add_a_notice_regarding_payment_schedule' | translate }}<mark translate>{{ 'app.admin.invoices.this_will_never_be_added_with_other_notices' }}</mark> {{ 'app.admin.invoices.eg_SE_to_schedules' | translate }}</td></tr>
|
||||
</table>
|
||||
</script>
|
||||
|
@ -12,6 +12,7 @@
|
||||
<li ng-click="invoice.reference.help = 'addInvoiceNumber.html'">{{ 'app.admin.invoices.num_of_invoice' | translate }}</li>
|
||||
<li ng-click="invoice.reference.help = 'addOnlineInfo.html'">{{ 'app.admin.invoices.online_sales' | translate }}</li>
|
||||
<li ng-click="invoice.reference.help = 'addRefundInfo.html'">{{ 'app.admin.invoices.refund' | translate }}</li>
|
||||
<li ng-click="invoice.reference.help = 'addPaymentScheduleInfo.html'">{{ 'app.admin.invoices.payment_schedule' | translate }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
|
@ -1,11 +1,14 @@
|
||||
<div class="widget panel b-a m m-t-lg" ng-if="user && !events.modifiable && !events.moved">
|
||||
<div class="widget panel b-a m m-t-lg" ng-if="user && !events.modifiable && !events.moved && !paidPlan">
|
||||
<div class="panel-heading b-b small">
|
||||
<h3 translate>{{ 'app.shared.cart.summary' }}</h3>
|
||||
</div>
|
||||
|
||||
<div class="widget-content no-bg auto wrapper" ng-show="events.reserved.length == 0 && (!events.paid || events.paid.length == 0)">
|
||||
<p class="font-felt fleche-left text-lg"><img src="../../images/arrow-left.png" class="fleche-left visible-lg" />
|
||||
{{ 'app.shared.cart.select_one_or_more_slots_in_the_calendar' | translate:{SINGLE:limitToOneSlot} }}</p>
|
||||
<div class="widget-content no-bg auto wrapper" ng-show="(!events.reserved && !selectedPlan) || events.reserved.length == 0 && (!events.paid || events.paid.length == 0)">
|
||||
<p class="font-felt fleche-left text-lg">
|
||||
<img src="../../images/arrow-left.png" class="fleche-left visible-lg" />
|
||||
<span ng-show="events.reserved">{{ 'app.shared.cart.select_one_or_more_slots_in_the_calendar' | translate:{SINGLE:limitToOneSlot} }}</span>
|
||||
<span ng-hide="events.reserved" translate>{{ 'app.shared.cart.select_a_plan' }}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="widget-content no-bg auto wrapper" ng-if="events.reserved.length > 0">
|
||||
|
@ -14,10 +14,36 @@ class PaymentSchedule < ApplicationRecord
|
||||
|
||||
has_many :payment_schedule_items
|
||||
|
||||
before_create :add_environment
|
||||
after_create :update_reference, :chain_record
|
||||
|
||||
##
|
||||
# This is useful to check the first item because its amount may be different from the others
|
||||
##
|
||||
def ordered_items
|
||||
payment_schedule_items.order(due_date: :asc)
|
||||
end
|
||||
|
||||
def add_environment
|
||||
self.environment = Rails.env
|
||||
end
|
||||
|
||||
def update_reference
|
||||
self.reference = InvoiceReferenceService.generate_reference(self, payment_schedule: true)
|
||||
save
|
||||
end
|
||||
|
||||
def chain_record
|
||||
self.footprint = compute_footprint
|
||||
save!
|
||||
FootprintDebug.create!(
|
||||
footprint: footprint,
|
||||
data: FootprintService.footprint_data(PaymentSchedule, self),
|
||||
klass: PaymentSchedule.name
|
||||
)
|
||||
end
|
||||
|
||||
def compute_footprint
|
||||
FootprintService.compute_footprint(PaymentSchedule, self)
|
||||
end
|
||||
end
|
||||
|
@ -51,11 +51,10 @@ class Subscription < ApplicationRecord
|
||||
|
||||
def generate_schedule(operator_profile_id, coupon_code = nil, payment_intent_id = nil)
|
||||
operator = InvoicingProfile.find(operator_profile_id)&.user
|
||||
method = operator&.admin? || (operator&.manager? && operator != user) ? nil : 'stripe'
|
||||
method = operator&.admin? || (operator&.manager? && operator != user) ? nil : 'stripe' # FIXME, paiement à l'accueil
|
||||
coupon = Coupon.find_by(code: coupon_code) unless coupon_code.nil?
|
||||
|
||||
schedule = PaymentScheduleService.new.create(self, plan.amount, coupon: coupon, operator: operator, payment_method: method)
|
||||
|
||||
schedule = PaymentScheduleService.new.create(self, plan.amount, coupon: coupon, operator: operator, payment_method: method, user: user)
|
||||
end
|
||||
|
||||
def generate_invoice(operator_profile_id, coupon_code = nil, payment_intent_id = nil)
|
||||
|
@ -1,12 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Provides methods to generate invoice references
|
||||
# Provides methods to generate Invoice or PaymentSchedule references
|
||||
class InvoiceReferenceService
|
||||
class << self
|
||||
def generate_reference(invoice, date: DateTime.current, avoir: false)
|
||||
def generate_reference(invoice, date: DateTime.current, avoir: false, payment_schedule: false)
|
||||
pattern = Setting.get('invoice_reference')
|
||||
|
||||
reference = replace_invoice_number_pattern(pattern, invoice)
|
||||
reference = replace_invoice_number_pattern(pattern)
|
||||
reference = replace_date_pattern(reference, date)
|
||||
|
||||
if avoir
|
||||
@ -15,6 +15,13 @@ class InvoiceReferenceService
|
||||
|
||||
# remove information about online selling (X[text])
|
||||
reference.gsub!(/X\[([^\]]+)\]/, ''.to_s)
|
||||
elsif payment_schedule
|
||||
# information about payment schedule
|
||||
reference.gsub!(/S\[([^\]]+)\]/, '\1')
|
||||
# remove information about online selling (X[text])
|
||||
reference.gsub!(/X\[([^\]]+)\]/, ''.to_s)
|
||||
# remove information about refunds (R[text])
|
||||
reference.gsub!(/R\[([^\]]+)\]/, ''.to_s)
|
||||
else
|
||||
# information about online selling (X[text])
|
||||
if invoice.paid_with_stripe?
|
||||
@ -35,10 +42,10 @@ class InvoiceReferenceService
|
||||
|
||||
# global invoice number (nn..nn)
|
||||
reference = pattern.gsub(/n+(?![^\[]*\])/) do |match|
|
||||
pad_and_truncate(number_of_invoices(invoice, 'global'), match.to_s.length)
|
||||
pad_and_truncate(number_of_invoices('global'), match.to_s.length)
|
||||
end
|
||||
|
||||
reference = replace_invoice_number_pattern(reference, invoice)
|
||||
reference = replace_invoice_number_pattern(reference)
|
||||
replace_date_pattern(reference, invoice.created_at)
|
||||
end
|
||||
|
||||
@ -57,11 +64,10 @@ class InvoiceReferenceService
|
||||
##
|
||||
# Returns the number of current invoices in the given range around the current date.
|
||||
# If range is invalid or not specified, the total number of invoices is returned.
|
||||
# @param invoice {Invoice}
|
||||
# @param range {String} 'day', 'month', 'year'
|
||||
# @return {Integer}
|
||||
##
|
||||
def number_of_invoices(invoice, range)
|
||||
def number_of_invoices(range)
|
||||
case range.to_s
|
||||
when 'day'
|
||||
start = DateTime.current.beginning_of_day
|
||||
@ -73,7 +79,7 @@ class InvoiceReferenceService
|
||||
start = DateTime.current.beginning_of_year
|
||||
ending = DateTime.current.end_of_year
|
||||
else
|
||||
return invoice.id
|
||||
return get_max_id(Invoice) + get_max_id(PaymentSchedule)
|
||||
end
|
||||
return Invoice.count unless defined? start && defined? ending
|
||||
|
||||
@ -111,25 +117,32 @@ class InvoiceReferenceService
|
||||
##
|
||||
# Replace the invoice number elements in the provided pattern with counts from the database
|
||||
# @param reference {string}
|
||||
# @param invoice {Invoice}
|
||||
##
|
||||
def replace_invoice_number_pattern(reference, invoice)
|
||||
def replace_invoice_number_pattern(reference)
|
||||
copy = reference.dup
|
||||
|
||||
# invoice number per year (yy..yy)
|
||||
copy.gsub!(/y+(?![^\[]*\])/) do |match|
|
||||
pad_and_truncate(number_of_invoices(invoice, 'year'), match.to_s.length)
|
||||
pad_and_truncate(number_of_invoices('year'), match.to_s.length)
|
||||
end
|
||||
# invoice number per month (mm..mm)
|
||||
copy.gsub!(/m+(?![^\[]*\])/) do |match|
|
||||
pad_and_truncate(number_of_invoices(invoice, 'month'), match.to_s.length)
|
||||
pad_and_truncate(number_of_invoices('month'), match.to_s.length)
|
||||
end
|
||||
# invoice number per day (dd..dd)
|
||||
copy.gsub!(/d+(?![^\[]*\])/) do |match|
|
||||
pad_and_truncate(number_of_invoices(invoice, 'day'), match.to_s.length)
|
||||
pad_and_truncate(number_of_invoices('day'), match.to_s.length)
|
||||
end
|
||||
|
||||
copy
|
||||
end
|
||||
|
||||
##
|
||||
# Return the maximum ID from the database, for the given class
|
||||
# @param klass {ActiveRecord::Base}
|
||||
##
|
||||
def get_max_id(klass)
|
||||
ActiveRecord::Base.connection.execute("SELECT max(id) FROM #{klass.table_name}").getvalue(0, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -46,21 +46,22 @@ class PaymentScheduleService
|
||||
{ payment_schedule: ps, items: items }
|
||||
end
|
||||
|
||||
def create(subscription, total, coupon: nil, operator: nil, payment_method: nil, reservation: nil)
|
||||
def create(subscription, total, coupon: nil, operator: nil, payment_method: nil, reservation: nil, user: nil)
|
||||
schedule = compute(subscription.plan, total, coupon)
|
||||
ps = schedule[:payment_schedule]
|
||||
items = schedule[:items]
|
||||
|
||||
ps.scheduled = subscription
|
||||
ps.payment_method = payment_method
|
||||
ps.operator_profile_id = operator
|
||||
ps.operator_profile = operator.invoicing_profile
|
||||
ps.invoicing_profile = user.invoicing_profile
|
||||
ps.save!
|
||||
# TODO, fields: reference, wallet_amount, wallet_transaction_id, footprint, environment, invoicing_profile
|
||||
items.each do |item|
|
||||
item.payment_schedule = ps
|
||||
item.save!
|
||||
end
|
||||
|
||||
StripeWorker.perform_async(:create_stripe_subscription, ps.id, reservation&.reservable&.stp_product_id)
|
||||
ps
|
||||
end
|
||||
end
|
||||
|
@ -9,7 +9,14 @@ class Subscriptions::Subscribe
|
||||
@operator_profile_id = operator_profile_id
|
||||
end
|
||||
|
||||
def pay_and_save(subscription, coupon: nil, invoice: nil, payment_intent_id: nil, schedule: nil)
|
||||
##
|
||||
# @param subscription {Subscription}
|
||||
# @param coupon {String} coupon code
|
||||
# @param invoice {Boolean}
|
||||
# @param payment_intent_id {String} from stripe
|
||||
# @param schedule {Boolean}
|
||||
##
|
||||
def pay_and_save(subscription, coupon: nil, invoice: false, payment_intent_id: nil, schedule: false)
|
||||
return false if user_id.nil?
|
||||
|
||||
subscription.statistic_profile_id = StatisticProfile.find_by(user_id: user_id).id
|
||||
|
@ -76,22 +76,22 @@ class StripeWorker
|
||||
|
||||
items = []
|
||||
if first_item.amount != second_item.amount
|
||||
if first_item.details[:adjustment]
|
||||
unless first_item.details['adjustment']&.zero?
|
||||
# adjustment: when dividing the price of the plan / months, sometimes it forces us to round the amount per month.
|
||||
# The difference is invoiced here
|
||||
p1 = Stripe::Price.create({
|
||||
unit_amount: first_item.details[:adjustment],
|
||||
unit_amount: first_item.details['adjustment'],
|
||||
currency: Setting.get('stripe_currency'),
|
||||
product: payment_schedule.scheduled.plan.stp_product_id,
|
||||
nickname: "Price adjustment payment schedule #{payment_schedule_id}"
|
||||
nickname: "Price adjustment for payment schedule #{payment_schedule_id}"
|
||||
}, { api_key: Setting.get('stripe_secret_key') })
|
||||
items.push(price: p1[:id])
|
||||
end
|
||||
if first_item.details[:other_items]
|
||||
unless first_item.details['other_items']&.zero?
|
||||
# when taking a subscription at the same time of a reservation (space, machine or training), the amount of the
|
||||
# reservation is invoiced here.
|
||||
p2 = Stripe::Price.create({
|
||||
unit_amount: first_item.details[:other_items],
|
||||
unit_amount: first_item.details['other_items'],
|
||||
currency: Setting.get('stripe_currency'),
|
||||
product: reservable_stp_id,
|
||||
nickname: "Reservations for payment schedule #{payment_schedule_id}"
|
||||
@ -102,7 +102,7 @@ class StripeWorker
|
||||
|
||||
# subscription (recurring price)
|
||||
price = Stripe::Price.create({
|
||||
unit_amount: first_item.details[:recurring],
|
||||
unit_amount: first_item.details['recurring'],
|
||||
currency: Setting.get('stripe_currency'),
|
||||
recurring: { interval: 'month', interval_count: 1 },
|
||||
product: payment_schedule.scheduled.plan.stp_product_id
|
||||
|
@ -440,6 +440,7 @@ en:
|
||||
important_notes: "Important notes"
|
||||
address_and_legal_information: "Address and legal information"
|
||||
invoice_reference: "Invoice reference"
|
||||
text: "text"
|
||||
year: "Year"
|
||||
month: "Month"
|
||||
day: "Day"
|
||||
@ -447,6 +448,7 @@ en:
|
||||
online_sales: "Online sales"
|
||||
wallet: "Wallet"
|
||||
refund: "Refund"
|
||||
payment_schedule: "Payment schedule"
|
||||
model: "Model"
|
||||
documentation: "Documentation"
|
||||
2_digits_year: "2 digits year (eg. 70)"
|
||||
@ -469,9 +471,10 @@ en:
|
||||
eg_XVL_will_add_VL_to_the_invoices_settled_with_stripe: '(eg. X[/VL] will add "/VL" to the invoices settled with stripe)'
|
||||
add_a_notice_regarding_refunds_only_if_the_invoice_is_concerned: "Add a notice regarding refunds, only if the invoice is concerned."
|
||||
this_will_never_be_added_when_an_online_sales_notice_is_present: "This will never be added when an online sales notice is present."
|
||||
eg_RA_will_add_A_to_the_refund_invoices: '(ed. R[/A] will add "/A" to the refund invoices)'
|
||||
add_a_notice_regarding_the_wallet_only_if_the_invoice_is_concerned: "Add a notice regarding the wallet, only if the invoice is concerned."
|
||||
eg_WPM_will_add_PM_to_the_invoices_settled_with_wallet: '(eg. W[/PM] will add "/PM" to the invoices settled with wallet)'
|
||||
eg_RA_will_add_A_to_the_refund_invoices: '(eg. R[/A] will add "/A" to the refund invoices)'
|
||||
add_a_notice_regarding_payment_schedule: "Add a notice regarding the payment schedules, only for concerned documents."
|
||||
this_will_never_be_added_with_other_notices: "This will never be added when any other notice is present."
|
||||
eg_SE_to_schedules: '(eg. S[/E] will add "/E" to the payment schedules)'
|
||||
code: "Code"
|
||||
enable_the_code: "Enable the code"
|
||||
enabled: "Enabled"
|
||||
|
@ -440,6 +440,7 @@ fr:
|
||||
important_notes: "Informations importantes"
|
||||
address_and_legal_information: "Adresse et informations légales"
|
||||
invoice_reference: "Référence facture"
|
||||
text: "texte"
|
||||
year: "Année"
|
||||
month: "Mois"
|
||||
day: "Jour"
|
||||
@ -447,6 +448,7 @@ fr:
|
||||
online_sales: "Vente en ligne"
|
||||
wallet: "Porte-monnaie"
|
||||
refund: "Remboursement"
|
||||
payment_schedule: "Échéancier"
|
||||
model: "Modèle"
|
||||
documentation: "Documentation"
|
||||
2_digits_year: "Année sur 2 chiffres (ex. 70)"
|
||||
@ -470,8 +472,9 @@ fr:
|
||||
add_a_notice_regarding_refunds_only_if_the_invoice_is_concerned: "Ajoute une information relative aux remboursements, uniquement si cela concerne la facture."
|
||||
this_will_never_be_added_when_an_online_sales_notice_is_present: "Ceci ne sera jamais cumulé avec une information de vente en ligne."
|
||||
eg_RA_will_add_A_to_the_refund_invoices: '(ex. R[/A] ajoutera "/A" aux factures de remboursement)'
|
||||
add_a_notice_regarding_the_wallet_only_if_the_invoice_is_concerned: "Ajoute une information relative au paiement par le porte-monnaie, uniquement si cela concerne la facture."
|
||||
eg_WPM_will_add_PM_to_the_invoices_settled_with_wallet: '(ex. W[/PM] ajoutera "/PM" aux factures réglées avec porte-monnaie)'
|
||||
add_a_notice_regarding_payment_schedule: "Ajoute une information relative aux échéanciers de paiement, uniquement pour les documents concernés."
|
||||
this_will_never_be_added_with_other_notices: "Ceci ne sera jamais cumulé avec une autre information (remboursement ou vente en ligne)."
|
||||
eg_SE_to_schedules: '(ex. S[/É] ajoutera "/É" aux échéanciers de paiement)'
|
||||
code: "Code"
|
||||
enable_the_code: "Activer le code"
|
||||
enabled: "Activé"
|
||||
|
@ -386,6 +386,7 @@ en:
|
||||
cart:
|
||||
summary: "Summary"
|
||||
select_one_or_more_slots_in_the_calendar: "Select one {SINGLE, select, true{slot} other{or more slots}} in the calendar"
|
||||
select_a_plan: "Select a plan here"
|
||||
you_ve_just_selected_the_slot: "You've just selected the slot:"
|
||||
datetime_to_time: "{START_DATETIME} to {END_TIME}" #eg: Thursday, September 4, 1986 8:30 PM to 10:00 PM
|
||||
cost_of_TYPE: "Cost of the {TYPE, select, Machine{machine slot} Training{training} Space{space slot} other{element}}"
|
||||
|
@ -386,6 +386,7 @@ fr:
|
||||
cart:
|
||||
summary: "Résumé"
|
||||
select_one_or_more_slots_in_the_calendar: "Sélectionnez un {SINGLE, select, true{créneau} other{ou plusieurs créneaux}} dans le calendrier"
|
||||
select_a_plan: "Sélectionnez une formule d'abonnement ici"
|
||||
you_ve_just_selected_the_slot: "Vous venez de sélectionner le créneau :"
|
||||
datetime_to_time: "{START_DATETIME} à {END_TIME}" #eg: Thursday, September 4, 1986 8:30 PM to 10:00 PM
|
||||
cost_of_TYPE: "Coût {TYPE, select, Machine{du créneau machine} Training{de la formation} Space{du créneau espace} other{de l'élément}}"
|
||||
|
@ -588,7 +588,7 @@ unless Setting.find_by(name: 'invoice_logo').try(:value)
|
||||
setting.save
|
||||
end
|
||||
|
||||
Setting.set('invoice_reference', 'YYMMmmmX[/VL]R[/A]') unless Setting.find_by(name: 'invoice_reference').try(:value)
|
||||
Setting.set('invoice_reference', 'YYMMmmmX[/VL]R[/A]S[/E]') unless Setting.find_by(name: 'invoice_reference').try(:value)
|
||||
|
||||
Setting.set('invoice_code-active', true) unless Setting.find_by(name: 'invoice_code-active').try(:value)
|
||||
|
||||
|
@ -101,6 +101,13 @@ namespace :fablab do
|
||||
FileUtils.mv 'tmp/invoices', 'invoices'
|
||||
end
|
||||
|
||||
desc 'add model for payment-schedules reference'
|
||||
task add_schedule_reference: :environment do
|
||||
setting = Setting.find_by(name: 'invoice_reference')
|
||||
current = setting.value
|
||||
setting.value = "#{current}S[/E]" unless /S\[([^\]]+)\]/.match?(current)
|
||||
end
|
||||
|
||||
desc 'migrate environment variables to the database (settings)'
|
||||
task env_to_db: :environment do
|
||||
include ApplicationHelper
|
||||
|
2
test/fixtures/history_values.yml
vendored
2
test/fixtures/history_values.yml
vendored
@ -100,7 +100,7 @@ value_history_10:
|
||||
id: 10
|
||||
setting_id: 10
|
||||
invoicing_profile_id: 1
|
||||
value: YYMMmmmX[/VL]R[/A]
|
||||
value: YYMMmmmX[/VL]R[/A]S[/E]
|
||||
created_at: 2018-12-17 11:23:01.603733000 Z
|
||||
updated_at: 2018-12-17 11:23:01.603733000 Z
|
||||
footprint: ed23a2eb1903befc977621bc3c3b19aad831fe550ebaa99e9299238b3d93c275
|
||||
|
Loading…
x
Reference in New Issue
Block a user