mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-12-01 12:24:28 +01:00
Merge branch 'monthly-payment' into staging
This commit is contained in:
commit
92b1fe165f
@ -1,6 +1,7 @@
|
|||||||
# Changelog Fab-manager
|
# Changelog Fab-manager
|
||||||
|
|
||||||
## Next release
|
## Next release
|
||||||
|
- Payment schedules on subscriptions
|
||||||
- Refactored theme builder to use scss files
|
- Refactored theme builder to use scss files
|
||||||
- Updated stripe gem to 5.29.0
|
- Updated stripe gem to 5.29.0
|
||||||
- Architecture documentation
|
- Architecture documentation
|
||||||
|
@ -735,11 +735,21 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
|||||||
placement: 'left'
|
placement: 'left'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (settings.invoicing_module === 'true') {
|
||||||
|
uitour.createStep({
|
||||||
|
selector: '.invoices-management .payment-schedules-list',
|
||||||
|
stepId: 'payment-schedules',
|
||||||
|
order: 5,
|
||||||
|
title: _t('app.admin.tour.invoices.payment-schedules.title'),
|
||||||
|
content: _t('app.admin.tour.invoices.payment-schedules.content'),
|
||||||
|
placement: 'bottom'
|
||||||
|
});
|
||||||
|
}
|
||||||
if (AuthService.isAuthorized('admin')) {
|
if (AuthService.isAuthorized('admin')) {
|
||||||
uitour.createStep({
|
uitour.createStep({
|
||||||
selector: '.invoices-management .invoices-settings',
|
selector: '.invoices-management .invoices-settings',
|
||||||
stepId: 'settings',
|
stepId: 'settings',
|
||||||
order: 5,
|
order: 6,
|
||||||
title: _t('app.admin.tour.invoices.settings.title'),
|
title: _t('app.admin.tour.invoices.settings.title'),
|
||||||
content: _t('app.admin.tour.invoices.settings.content'),
|
content: _t('app.admin.tour.invoices.settings.content'),
|
||||||
placement: 'bottom'
|
placement: 'bottom'
|
||||||
@ -747,7 +757,7 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
|||||||
uitour.createStep({
|
uitour.createStep({
|
||||||
selector: '.invoices-management .accounting-codes-tab',
|
selector: '.invoices-management .accounting-codes-tab',
|
||||||
stepId: 'codes',
|
stepId: 'codes',
|
||||||
order: 6,
|
order: 7,
|
||||||
title: _t('app.admin.tour.invoices.codes.title'),
|
title: _t('app.admin.tour.invoices.codes.title'),
|
||||||
content: _t('app.admin.tour.invoices.codes.content'),
|
content: _t('app.admin.tour.invoices.codes.content'),
|
||||||
placement: 'bottom'
|
placement: 'bottom'
|
||||||
@ -755,7 +765,7 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
|||||||
uitour.createStep({
|
uitour.createStep({
|
||||||
selector: '.heading .export-accounting-button',
|
selector: '.heading .export-accounting-button',
|
||||||
stepId: 'export',
|
stepId: 'export',
|
||||||
order: 7,
|
order: 8,
|
||||||
title: _t('app.admin.tour.invoices.export.title'),
|
title: _t('app.admin.tour.invoices.export.title'),
|
||||||
content: _t('app.admin.tour.invoices.export.content'),
|
content: _t('app.admin.tour.invoices.export.content'),
|
||||||
placement: 'bottom'
|
placement: 'bottom'
|
||||||
@ -763,7 +773,7 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
|||||||
uitour.createStep({
|
uitour.createStep({
|
||||||
selector: '.invoices-management .payment-settings',
|
selector: '.invoices-management .payment-settings',
|
||||||
stepId: 'payment',
|
stepId: 'payment',
|
||||||
order: 8,
|
order: 9,
|
||||||
title: _t('app.admin.tour.invoices.payment.title'),
|
title: _t('app.admin.tour.invoices.payment.title'),
|
||||||
content: _t('app.admin.tour.invoices.payment.content'),
|
content: _t('app.admin.tour.invoices.payment.content'),
|
||||||
placement: 'bottom',
|
placement: 'bottom',
|
||||||
@ -772,7 +782,7 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
|||||||
uitour.createStep({
|
uitour.createStep({
|
||||||
selector: '.heading .close-accounting-periods-button',
|
selector: '.heading .close-accounting-periods-button',
|
||||||
stepId: 'periods',
|
stepId: 'periods',
|
||||||
order: 9,
|
order: 10,
|
||||||
title: _t('app.admin.tour.invoices.periods.title'),
|
title: _t('app.admin.tour.invoices.periods.title'),
|
||||||
content: _t('app.admin.tour.invoices.periods.content'),
|
content: _t('app.admin.tour.invoices.periods.content'),
|
||||||
placement: 'bottom',
|
placement: 'bottom',
|
||||||
@ -782,7 +792,7 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
|||||||
uitour.createStep({
|
uitour.createStep({
|
||||||
selector: 'body',
|
selector: 'body',
|
||||||
stepId: 'conclusion',
|
stepId: 'conclusion',
|
||||||
order: 10,
|
order: 11,
|
||||||
title: _t('app.admin.tour.conclusion.title'),
|
title: _t('app.admin.tour.conclusion.title'),
|
||||||
content: _t('app.admin.tour.conclusion.content'),
|
content: _t('app.admin.tour.conclusion.content'),
|
||||||
placement: 'bottom',
|
placement: 'bottom',
|
||||||
@ -790,7 +800,7 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
|||||||
});
|
});
|
||||||
// on step change, change the active tab if needed
|
// on step change, change the active tab if needed
|
||||||
uitour.on('stepChanged', function (nextStep) {
|
uitour.on('stepChanged', function (nextStep) {
|
||||||
if (nextStep.stepId === 'list' || nextStep.stepId === 'settings') {
|
if (nextStep.stepId === 'list' || nextStep.stepId === 'refund') {
|
||||||
$scope.tabs.active = 0;
|
$scope.tabs.active = 0;
|
||||||
}
|
}
|
||||||
if (nextStep.stepId === 'settings') {
|
if (nextStep.stepId === 'settings') {
|
||||||
@ -802,6 +812,9 @@ Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'I
|
|||||||
if (nextStep.stepId === 'payment') {
|
if (nextStep.stepId === 'payment') {
|
||||||
$scope.tabs.active = 3;
|
$scope.tabs.active = 3;
|
||||||
}
|
}
|
||||||
|
if (nextStep.stepId === 'payment-schedules') {
|
||||||
|
$scope.tabs.active = 4;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// on tour end, save the status in database
|
// on tour end, save the status in database
|
||||||
uitour.on('ended', function () {
|
uitour.on('ended', function () {
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
<ng-include src="'/admin/invoices/list.html'"></ng-include>
|
<ng-include src="'/admin/invoices/list.html'"></ng-include>
|
||||||
</uib-tab>
|
</uib-tab>
|
||||||
|
|
||||||
<uib-tab heading="{{ 'app.admin.invoices.payment_schedules_list' | translate }}" ng-show="$root.modules.invoicing" index="4">
|
<uib-tab heading="{{ 'app.admin.invoices.payment_schedules_list' | translate }}" ng-show="$root.modules.invoicing" index="4" class="payment-schedules-list">
|
||||||
<payment-schedules-list current-user="currentUser" />
|
<payment-schedules-list current-user="currentUser" />
|
||||||
</uib-tab>
|
</uib-tab>
|
||||||
|
|
||||||
|
@ -51,6 +51,12 @@ class PaymentSchedule < PaymentDocument
|
|||||||
invoicing_profile.user
|
invoicing_profile.user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# for debug & used by rake task "fablab:maintenance:regenerate_schedules"
|
||||||
|
def regenerate_pdf
|
||||||
|
pdf = ::PDF::PaymentSchedule.new(self).render
|
||||||
|
File.binwrite(file, pdf)
|
||||||
|
end
|
||||||
|
|
||||||
def check_footprint
|
def check_footprint
|
||||||
payment_schedule_items.map(&:check_footprint).all? && footprint == compute_footprint
|
payment_schedule_items.map(&:check_footprint).all? && footprint == compute_footprint
|
||||||
end
|
end
|
||||||
|
@ -85,6 +85,12 @@ class Subscription < ApplicationRecord
|
|||||||
statistic_profile.user
|
statistic_profile.user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def original_payment_schedule
|
||||||
|
return payment_schedule if payment_schedule
|
||||||
|
|
||||||
|
PaymentScheduleItem.where("cast(details->>'subscription_id' AS int) = ?", id).first&.payment_schedule
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def notify_member_subscribed_plan
|
def notify_member_subscribed_plan
|
||||||
|
@ -53,7 +53,7 @@ class Subscriptions::Subscribe
|
|||||||
expiration_date: new_expiration_date
|
expiration_date: new_expiration_date
|
||||||
)
|
)
|
||||||
if new_sub.save
|
if new_sub.save
|
||||||
schedule = subscription.payment_schedule
|
schedule = subscription.original_payment_schedule
|
||||||
details = Price.compute(true, new_sub.user, nil, [], plan_id: subscription.plan_id)
|
details = Price.compute(true, new_sub.user, nil, [], plan_id: subscription.plan_id)
|
||||||
payment = if schedule
|
payment = if schedule
|
||||||
generate_schedule(subscription: new_sub,
|
generate_schedule(subscription: new_sub,
|
||||||
|
@ -254,7 +254,8 @@ h5:after {
|
|||||||
color: $secondary-text-color;
|
color: $secondary-text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pricing-panel .content .wrap {
|
.pricing-panel .plan-card .content .wrap,
|
||||||
|
.pricing-panel .plan-card .content .wrap-monthly {
|
||||||
border-color: $secondary;
|
border-color: $secondary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -824,7 +824,7 @@ en:
|
|||||||
credits_will_remain_unchanged: "The balance of free credits (training / machines / spaces) of the user will remain unchanged."
|
credits_will_remain_unchanged: "The balance of free credits (training / machines / spaces) of the user will remain unchanged."
|
||||||
you_intentionally_decide_to_extend_the_user_s_subscription_by_charging_him_again_for_his_current_subscription: "You intentionally decide to extend the user's subscription by charging him again for his current subscription."
|
you_intentionally_decide_to_extend_the_user_s_subscription_by_charging_him_again_for_his_current_subscription: "You intentionally decide to extend the user's subscription by charging him again for his current subscription."
|
||||||
credits_will_be_reset: "The balance of free credits (training / machines / spaces) of the user will be reset, unused credits will be lost."
|
credits_will_be_reset: "The balance of free credits (training / machines / spaces) of the user will be reset, unused credits will be lost."
|
||||||
payment_scheduled: "If the previous subscription was charged through a payment schedule, this one will be charged the same way."
|
payment_scheduled: "If the previous subscription was charged through a payment schedule, this one will be charged the same way, the first deadline being charged right now, then each following month."
|
||||||
until_expiration_date: "Until (expiration date):"
|
until_expiration_date: "Until (expiration date):"
|
||||||
you_successfully_changed_the_expiration_date_of_the_user_s_subscription: "You successfully changed the expiration date of the user's subscription"
|
you_successfully_changed_the_expiration_date_of_the_user_s_subscription: "You successfully changed the expiration date of the user's subscription"
|
||||||
a_problem_occurred_while_saving_the_date: "A problem occurred while saving the date."
|
a_problem_occurred_while_saving_the_date: "A problem occurred while saving the date."
|
||||||
@ -1332,6 +1332,9 @@ en:
|
|||||||
refund:
|
refund:
|
||||||
title: "Credit note"
|
title: "Credit note"
|
||||||
content: "Allows you to generate a credit note for the invoice on this line or some of its sub-elements. <strong>Warning:</strong> This will only generate the accounting document, the actual refund of the user will always be your responsibility."
|
content: "Allows you to generate a credit note for the invoice on this line or some of its sub-elements. <strong>Warning:</strong> This will only generate the accounting document, the actual refund of the user will always be your responsibility."
|
||||||
|
payment-schedules:
|
||||||
|
title: "Payment schedules"
|
||||||
|
content: "<p>Some subscription plans may be configured to allow the members to pay them with a monthly payment schedule.</p><p>Here you can view all existing payment schedules and manage their deadlines.</p><p>Click on [+] at the beginning of a row to display all deadlines associated with a payment schedule, and run some actions on them.</p>"
|
||||||
settings:
|
settings:
|
||||||
title: "Settings"
|
title: "Settings"
|
||||||
content: "<p>Here you can modify the parameters for invoices generation. Click on the item you are interested in to start editing.</p><p>In particular, this is where you can set if you are subject to VAT and the applicable rate.</p>"
|
content: "<p>Here you can modify the parameters for invoices generation. Click on the item you are interested in to start editing.</p><p>In particular, this is where you can set if you are subject to VAT and the applicable rate.</p>"
|
||||||
|
@ -824,7 +824,7 @@ fr:
|
|||||||
credits_will_remain_unchanged: "Le solde de crédits gratuits (formations/machines/espaces) de l'utilisateur restera inchangé."
|
credits_will_remain_unchanged: "Le solde de crédits gratuits (formations/machines/espaces) de l'utilisateur restera inchangé."
|
||||||
you_intentionally_decide_to_extend_the_user_s_subscription_by_charging_him_again_for_his_current_subscription: "Vous décidez délibérément d'étendre l'abonnement de l'utilisateur en lui faisant repayer le prix de l'abonnement qu'il possède actuellement."
|
you_intentionally_decide_to_extend_the_user_s_subscription_by_charging_him_again_for_his_current_subscription: "Vous décidez délibérément d'étendre l'abonnement de l'utilisateur en lui faisant repayer le prix de l'abonnement qu'il possède actuellement."
|
||||||
credits_will_be_reset: "Le solde de crédits gratuits (formations/machines/espaces) de l'utilisateur sera remis à zéro, ses crédits non utilisés seront perdu."
|
credits_will_be_reset: "Le solde de crédits gratuits (formations/machines/espaces) de l'utilisateur sera remis à zéro, ses crédits non utilisés seront perdu."
|
||||||
payment_scheduled: "Si l'abonnement précédent a été facturé via un échéancier de paiement mensualisé, celui-ci sera facturé de la même façon."
|
payment_scheduled: "Si l'abonnement précédent a été facturé via un échéancier de paiement mensualisé, celui-ci sera facturé de la même façon, la première échéance étant facturée immédiatement, puis chaque mois suivant."
|
||||||
until_expiration_date: "Jusqu'à (date d'expiration) :"
|
until_expiration_date: "Jusqu'à (date d'expiration) :"
|
||||||
you_successfully_changed_the_expiration_date_of_the_user_s_subscription: "Vous avez bien modifié la date d'expiration de l'abonnement de l'utilisateur"
|
you_successfully_changed_the_expiration_date_of_the_user_s_subscription: "Vous avez bien modifié la date d'expiration de l'abonnement de l'utilisateur"
|
||||||
a_problem_occurred_while_saving_the_date: "Il y a eu un problème lors de l'enregistrement de la date."
|
a_problem_occurred_while_saving_the_date: "Il y a eu un problème lors de l'enregistrement de la date."
|
||||||
@ -1332,6 +1332,9 @@ fr:
|
|||||||
refund:
|
refund:
|
||||||
title: "Avoir"
|
title: "Avoir"
|
||||||
content: "Permet de générer un avoir sur la facture de cette ligne ou certains de ses sous-éléments. <strong>Attention :</strong> Cela générera uniquement le document comptable, le remboursement effectif de l'utilisateur restera toujours à votre charge."
|
content: "Permet de générer un avoir sur la facture de cette ligne ou certains de ses sous-éléments. <strong>Attention :</strong> Cela générera uniquement le document comptable, le remboursement effectif de l'utilisateur restera toujours à votre charge."
|
||||||
|
payment-schedules:
|
||||||
|
title: "Échéanciers de paiement"
|
||||||
|
content: "<p>Des formules d'abonnement peuvent être configurées pour permettre aux membres de les payer grâce à un échéancier de paiement mensuel.</p><p>Ici, vous pouvez voir tous les échéanciers de paiement existants et gérer leurs échéances.</p><p>Cliquez sur [+] au début d'une ligne pour afficher toutes les échéances associées à un échéancier, et effectuer des actions sur celles-ci.</p>"
|
||||||
settings:
|
settings:
|
||||||
title: "Paramètres"
|
title: "Paramètres"
|
||||||
content: "<p>Ici vous pourrez modifier les paramètres de génération des factures. Cliquez sur l'élément qui vous intéresse pour commencer l'édition.</p><p>C'est notamment ici que vous pourrez définir si vous êtes assujettis à la TVA et à quel taux.</p>"
|
content: "<p>Ici vous pourrez modifier les paramètres de génération des factures. Cliquez sur l'élément qui vous intéresse pour commencer l'édition.</p><p>C'est notamment ici que vous pourrez définir si vous êtes assujettis à la TVA et à quel taux.</p>"
|
||||||
|
@ -80,3 +80,4 @@ This is currently not supported, because of some PostgreSQL specific instruction
|
|||||||
- `db/migrate/20200623141305_update_search_vector_of_projects.rb` defines a PL/pgSQL function (`fill_search_vector_for_project()`) and create an SQL trigger for this function;
|
- `db/migrate/20200623141305_update_search_vector_of_projects.rb` defines a PL/pgSQL function (`fill_search_vector_for_project()`) and create an SQL trigger for this function;
|
||||||
- `db/migrate/20200629123011_update_pg_trgm.rb` is using [ALTER EXTENSION](https://www.postgresql.org/docs/10/sql-alterextension.html);
|
- `db/migrate/20200629123011_update_pg_trgm.rb` is using [ALTER EXTENSION](https://www.postgresql.org/docs/10/sql-alterextension.html);
|
||||||
- `db/migrate/20201027101809_create_payment_schedule_items.rb` is using [jsonb](https://www.postgresql.org/docs/9.4/static/datatype-json.html);
|
- `db/migrate/20201027101809_create_payment_schedule_items.rb` is using [jsonb](https://www.postgresql.org/docs/9.4/static/datatype-json.html);
|
||||||
|
- `app/models/subscription.rb@original_payment_schedule` is using `->>` and `cast(... AS int)` for querying a with jsonb column.
|
||||||
|
@ -17,6 +17,19 @@ namespace :fablab do
|
|||||||
puts '-> Done'
|
puts '-> Done'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
task :regenerate_schedules, %i[year month] => :environment do |_task, args|
|
||||||
|
year = args.year || Time.current.year
|
||||||
|
month = args.month || Time.current.month
|
||||||
|
start_date = Time.zone.local(year.to_i, month.to_i, 1)
|
||||||
|
end_date = start_date.next_month
|
||||||
|
puts "-> Start regenerate the payment schedules PDF between #{I18n.l start_date, format: :long} and " \
|
||||||
|
"#{I18n.l end_date - 1.minute, format: :long}"
|
||||||
|
schedules = PaymentSchedule.where('created_at >= :start_date AND created_at < :end_date', start_date: start_date, end_date: end_date)
|
||||||
|
.order(created_at: :asc)
|
||||||
|
schedules.each(&:regenerate_pdf)
|
||||||
|
puts '-> Done'
|
||||||
|
end
|
||||||
|
|
||||||
desc 'recreate every versions of images'
|
desc 'recreate every versions of images'
|
||||||
task build_images_versions: :environment do
|
task build_images_versions: :environment do
|
||||||
Project.find_each do |project|
|
Project.find_each do |project|
|
||||||
|
@ -26,7 +26,7 @@ add_mount()
|
|||||||
{
|
{
|
||||||
# shellcheck disable=SC2016
|
# shellcheck disable=SC2016
|
||||||
# we don't want to expand ${PWD}
|
# we don't want to expand ${PWD}
|
||||||
yq w docker-compose.yml "services.$SERVICE.volumes[+]" '- ${PWD}/payment_schedules:/usr/src/app/payment_schedules'
|
yq w -i docker-compose.yml "services.$SERVICE.volumes[+]" '${PWD}/payment_schedules:/usr/src/app/payment_schedules'
|
||||||
}
|
}
|
||||||
|
|
||||||
proceed()
|
proceed()
|
||||||
|
Loading…
Reference in New Issue
Block a user