1
0
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:
Sylvain 2020-11-16 16:37:40 +01:00
parent 881f8d3efb
commit 678af0de1f
21 changed files with 136 additions and 56 deletions

View File

@ -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

View File

@ -94,7 +94,6 @@ const PaymentScheduleSummaryWrapper: React.FC<PaymentScheduleSummaryProps> = ({
return (
<Loader>
<PaymentScheduleSummary schedule={schedule} $filter={$filter} />
<div>lorem ipsum</div>
</Loader>
);
}

View File

@ -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;
};

View File

@ -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/
*/

View File

@ -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;

View File

@ -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>

View File

@ -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">

View File

@ -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">

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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é"

View File

@ -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}}"

View File

@ -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}}"

View File

@ -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)

View File

@ -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

View File

@ -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