mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-19 13:54:25 +01:00
use localPaymentModal in cart directive
This commit is contained in:
parent
d43f719038
commit
19717d1351
@ -30,7 +30,7 @@ class API::StripeController < API::PaymentsController
|
||||
currency: Setting.get('stripe_currency'),
|
||||
confirmation_method: 'manual',
|
||||
confirm: true,
|
||||
customer: current_user.payment_gateway_object.gateway_object_id
|
||||
customer: cart.customer.payment_gateway_object.gateway_object_id
|
||||
}, { api_key: Setting.get('stripe_secret_key') }
|
||||
)
|
||||
elsif params[:payment_intent_id].present?
|
||||
|
@ -46,6 +46,7 @@ interface AbstractPaymentModalProps {
|
||||
formClassName?: string,
|
||||
title?: string,
|
||||
preventCgv?: boolean,
|
||||
preventScheduleInfo?: boolean,
|
||||
modalSize?: ModalSize,
|
||||
}
|
||||
|
||||
@ -55,7 +56,7 @@ interface AbstractPaymentModalProps {
|
||||
* This component must not be called directly but must be extended for each implemented payment gateway
|
||||
* @see https://reactjs.org/docs/composition-vs-inheritance.html
|
||||
*/
|
||||
export const AbstractPaymentModal: React.FC<AbstractPaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, cart, currentUser, schedule, customer, logoFooter, GatewayForm, formId, className, formClassName, title, preventCgv, modalSize }) => {
|
||||
export const AbstractPaymentModal: React.FC<AbstractPaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, cart, currentUser, schedule, customer, logoFooter, GatewayForm, formId, className, formClassName, title, preventCgv, preventScheduleInfo, modalSize }) => {
|
||||
// customer's wallet
|
||||
const [wallet, setWallet] = useState<Wallet>(null);
|
||||
// server-computed price with all details
|
||||
@ -129,10 +130,10 @@ export const AbstractPaymentModal: React.FC<AbstractPaymentModalProps> = ({ isOp
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we are currently creating a payment schedule
|
||||
* Check if we must display the info box about the payment schedule
|
||||
*/
|
||||
const isPaymentSchedule = (): boolean => {
|
||||
return schedule !== undefined;
|
||||
const hasPaymentScheduleInfo = (): boolean => {
|
||||
return schedule !== undefined && !preventScheduleInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -202,7 +203,7 @@ export const AbstractPaymentModal: React.FC<AbstractPaymentModalProps> = ({ isOp
|
||||
{hasErrors() && <div className="payment-errors">
|
||||
{errors}
|
||||
</div>}
|
||||
{isPaymentSchedule() && <div className="payment-schedule-info">
|
||||
{hasPaymentScheduleInfo() && <div className="payment-schedule-info">
|
||||
<HtmlTranslate trKey="app.shared.payment.payment_schedule_html" options={{ DEADLINES: schedule.items.length, GATEWAY: gateway }} />
|
||||
</div>}
|
||||
{hasCgv() && <div className="terms-of-sales">
|
||||
@ -233,5 +234,6 @@ export const AbstractPaymentModal: React.FC<AbstractPaymentModalProps> = ({ isOp
|
||||
AbstractPaymentModal.defaultProps = {
|
||||
title: 'app.shared.payment.online_payment',
|
||||
preventCgv: false,
|
||||
preventScheduleInfo: false,
|
||||
modalSize: ModalSize.medium
|
||||
};
|
||||
|
@ -103,10 +103,12 @@ export const LocalPaymentForm: React.FC<GatewayFormProps> = ({ onSubmit, onSucce
|
||||
{!paymentSchedule && <p className="payment">{t('app.admin.local_payment.about_to_cash')}</p>}
|
||||
{paymentSchedule && <div className="payment-schedule">
|
||||
<div className="schedule-method">
|
||||
<label htmlFor="payment-method">{t('app.admin.local_payment.payment_method')}</label>
|
||||
<Select placeholder={ t('app.admin.local_payment.payment_method') }
|
||||
id="payment-method"
|
||||
className="method-select"
|
||||
onChange={handleUpdateMethod}
|
||||
options={buildMethodOptions}
|
||||
options={buildMethodOptions()}
|
||||
defaultValue={methodToOption(method)} />
|
||||
{method === 'card' && <p>{t('app.admin.local_payment.card_collection_info')}</p>}
|
||||
{method === 'check' && <p>{t('app.admin.local_payment.check_collection_info', { DEADLINES: paymentSchedule.items.length })}</p>}
|
||||
@ -115,7 +117,7 @@ export const LocalPaymentForm: React.FC<GatewayFormProps> = ({ onSubmit, onSucce
|
||||
<ul>
|
||||
{paymentSchedule.items.map(item => {
|
||||
return (
|
||||
<li key={item.id}>
|
||||
<li key={`${item.due_date}`}>
|
||||
<span className="schedule-item-date">{FormatLib.date(item.due_date)}</span>
|
||||
<span> </span>
|
||||
<span className="schedule-item-price">{FormatLib.price(item.amount)}</span>
|
||||
|
@ -7,7 +7,11 @@ import { User } from '../../../models/user';
|
||||
import { Invoice } from '../../../models/invoice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ModalSize } from '../../base/fab-modal';
|
||||
import { Loader } from '../../base/loader';
|
||||
import { react2angular } from 'react2angular';
|
||||
import { IApplication } from '../../../models/application';
|
||||
|
||||
declare var Application: IApplication;
|
||||
|
||||
interface LocalPaymentModalProps {
|
||||
isOpen: boolean,
|
||||
@ -22,7 +26,7 @@ interface LocalPaymentModalProps {
|
||||
/**
|
||||
* This component enables a privileged user to confirm a local payments.
|
||||
*/
|
||||
export const LocalPaymentModal: React.FC<LocalPaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, cart, currentUser, schedule, customer }) => {
|
||||
const LocalPaymentModalComponent: React.FC<LocalPaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, cart, currentUser, schedule, customer }) => {
|
||||
|
||||
const { t } = useTranslation('admin');
|
||||
|
||||
@ -71,6 +75,17 @@ export const LocalPaymentModal: React.FC<LocalPaymentModalProps> = ({ isOpen, to
|
||||
schedule={schedule}
|
||||
GatewayForm={renderForm}
|
||||
modalSize={schedule ? ModalSize.large : ModalSize.medium}
|
||||
preventCgv />
|
||||
preventCgv
|
||||
preventScheduleInfo />
|
||||
);
|
||||
}
|
||||
|
||||
export const LocalPaymentModal: React.FC<LocalPaymentModalProps> = ({ isOpen, toggleModal, afterSuccess, currentUser, schedule , cart, customer }) => {
|
||||
return (
|
||||
<Loader>
|
||||
<LocalPaymentModalComponent isOpen={isOpen} toggleModal={toggleModal} afterSuccess={afterSuccess} currentUser={currentUser} schedule={schedule} cart={cart} customer={customer} />
|
||||
</Loader>
|
||||
);
|
||||
}
|
||||
|
||||
Application.Components.component('localPaymentModal', react2angular(LocalPaymentModal, ['isOpen', 'toggleModal', 'afterSuccess', 'currentUser', 'schedule', 'cart', 'customer']));
|
||||
|
@ -79,6 +79,12 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
||||
cartItems: undefined
|
||||
};
|
||||
|
||||
// offline payments (at the fablab's reception)
|
||||
$scope.localPayment = {
|
||||
showModal: false,
|
||||
cartItems: undefined
|
||||
};
|
||||
|
||||
// currently logged-in user
|
||||
$scope.currentUser = $rootScope.currentUser;
|
||||
|
||||
@ -325,6 +331,19 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
||||
}, 50);
|
||||
};
|
||||
|
||||
/**
|
||||
* This will open/close the local payment modal
|
||||
*/
|
||||
$scope.toggleLocalPaymentModal = (beforeApply) => {
|
||||
setTimeout(() => {
|
||||
$scope.localPayment.showModal = !$scope.localPayment.showModal;
|
||||
if (typeof beforeApply === 'function') {
|
||||
beforeApply();
|
||||
}
|
||||
$scope.$apply();
|
||||
}, 50);
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoked atfer a successful card payment
|
||||
* @param invoice {*} may be an Invoice or a paymentSchedule
|
||||
@ -334,6 +353,15 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
||||
afterPayment(invoice);
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoked atfer a successful offline payment
|
||||
* @param invoice {*} may be an Invoice or a paymentSchedule
|
||||
*/
|
||||
$scope.afterLocalPaymentSuccess = (invoice) => {
|
||||
$scope.toggleLocalPaymentModal();
|
||||
afterPayment(invoice);
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoked when something wrong occurred during the payment dialog initialization
|
||||
* @param message {string}
|
||||
@ -717,195 +745,14 @@ Application.Directives.directive('cart', ['$rootScope', '$uibModal', 'dialogs',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a modal window that allows the user to process a local payment for his current shopping cart (admin only).
|
||||
*/
|
||||
const payOnSite = function (items) {
|
||||
$uibModal.open({
|
||||
templateUrl: '/shared/valid_reservation_modal.html',
|
||||
size: $scope.schedule.payment_schedule ? 'lg' : 'sm',
|
||||
resolve: {
|
||||
price () {
|
||||
return Price.compute(mkCartItems(items, '')).$promise;
|
||||
},
|
||||
cartItems () {
|
||||
return mkCartItems(items, '');
|
||||
},
|
||||
wallet () {
|
||||
return Wallet.getWalletByUser({ user_id: $scope.user.id }).$promise;
|
||||
},
|
||||
coupon () {
|
||||
return $scope.coupon.applied;
|
||||
},
|
||||
selectedPlan () {
|
||||
return $scope.selectedPlan;
|
||||
},
|
||||
schedule () {
|
||||
return $scope.schedule;
|
||||
},
|
||||
user () {
|
||||
return $scope.user;
|
||||
},
|
||||
settings () {
|
||||
return $scope.settings;
|
||||
}
|
||||
},
|
||||
controller: ['$scope', '$uibModalInstance', '$state', 'price', 'Auth', 'LocalPayment', 'wallet', 'helpers', '$filter', 'coupon', 'selectedPlan', 'schedule', 'cartItems', 'user', 'settings',
|
||||
function ($scope, $uibModalInstance, $state, price, Auth, LocalPayment, wallet, helpers, $filter, coupon, selectedPlan, schedule, cartItems, user, settings) {
|
||||
// user wallet amount
|
||||
$scope.wallet = wallet;
|
||||
|
||||
// Global price (total of all items)
|
||||
$scope.price = price.price;
|
||||
|
||||
// Price to pay (wallet deducted)
|
||||
$scope.amount = helpers.getAmountToPay(price.price, wallet.amount);
|
||||
|
||||
// Reservation &| subscription
|
||||
$scope.cartItems = cartItems;
|
||||
|
||||
// Subscription
|
||||
$scope.plan = selectedPlan;
|
||||
|
||||
// Used in wallet info template to interpolate some translations
|
||||
$scope.numberFilter = $filter('number');
|
||||
|
||||
// Shows the schedule info in the modal
|
||||
$scope.schedule = schedule.payment_schedule;
|
||||
|
||||
// how should we collect payments for the payment schedule
|
||||
$scope.method = {
|
||||
payment_method: 'card'
|
||||
};
|
||||
|
||||
// "valid" Button label
|
||||
$scope.validButtonName = '';
|
||||
|
||||
// online payment modal state
|
||||
// this is used to collect card data when a payment-schedule was selected, and paid with a card
|
||||
$scope.isOpenOnlinePaymentModal = false;
|
||||
|
||||
// the customer
|
||||
$scope.user = user;
|
||||
|
||||
/**
|
||||
* Check if the shopping cart contains a reservation
|
||||
* @return {Reservation|boolean}
|
||||
*/
|
||||
$scope.reservation = (function () {
|
||||
const item = cartItems.items.find(i => i.reservation);
|
||||
if (item && item.reservation.slots_attributes.length > 0) {
|
||||
return item.reservation;
|
||||
}
|
||||
return false;
|
||||
})();
|
||||
|
||||
/**
|
||||
* Check if the shopping cart contains a subscription
|
||||
* @return {Subscription|boolean}
|
||||
*/
|
||||
$scope.subscription = (function () {
|
||||
const item = cartItems.items.find(i => i.subscription);
|
||||
if (item && item.subscription.plan_id) {
|
||||
return item.subscription;
|
||||
}
|
||||
return false;
|
||||
})();
|
||||
|
||||
/**
|
||||
* Callback to process the local payment, triggered on button click
|
||||
*/
|
||||
$scope.ok = function () {
|
||||
if ($scope.schedule && $scope.method.payment_method === 'card') {
|
||||
// check that the online payment is enabled
|
||||
if (settings.online_payment_module !== 'true') {
|
||||
return growl.error(_t('app.shared.cart.online_payment_disabled'));
|
||||
} else {
|
||||
return $scope.toggleOnlinePaymentModal();
|
||||
}
|
||||
}
|
||||
$scope.attempting = true;
|
||||
LocalPayment.confirm(cartItems, function (reservation) {
|
||||
$uibModalInstance.close(reservation);
|
||||
$scope.attempting = true;
|
||||
}, function (response) {
|
||||
$scope.alerts = [];
|
||||
$scope.alerts.push({ msg: _t('app.shared.cart.a_problem_occurred_during_the_payment_process_please_try_again_later'), type: 'danger' });
|
||||
$scope.attempting = false;
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Callback to close the modal without processing the payment
|
||||
*/
|
||||
$scope.cancel = function () { $uibModalInstance.dismiss('cancel'); };
|
||||
|
||||
/**
|
||||
* Asynchronously updates the status of the online payment modal
|
||||
*/
|
||||
$scope.toggleOnlinePaymentModal = function () {
|
||||
setTimeout(() => {
|
||||
$scope.isOpenOnlinePaymentModal = !$scope.isOpenOnlinePaymentModal;
|
||||
$scope.$apply();
|
||||
}, 50);
|
||||
};
|
||||
|
||||
/**
|
||||
* After creating a payment schedule by card, from an administrator.
|
||||
* @param result {*} PaymentSchedule
|
||||
*/
|
||||
$scope.afterCreatePaymentSchedule = function (result) {
|
||||
$scope.toggleOnlinePaymentModal();
|
||||
$uibModalInstance.close(result);
|
||||
};
|
||||
|
||||
/**
|
||||
* Invoked when something wrong occurred during the payment dialog initialization
|
||||
* @param message {string}
|
||||
*/
|
||||
$scope.onCreatePaymentScheduleError = (message) => {
|
||||
growl.error(message);
|
||||
};
|
||||
|
||||
/* PRIVATE SCOPE */
|
||||
|
||||
/**
|
||||
* Kind of constructor: these actions will be realized first when the directive is loaded
|
||||
*/
|
||||
const initialize = function () {
|
||||
$scope.$watch('method.payment_method', function (newValue) {
|
||||
$scope.validButtonName = computeValidButtonName();
|
||||
$scope.cartItems.payment_method = newValue;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute the Label of the confirmation button
|
||||
*/
|
||||
const computeValidButtonName = function () {
|
||||
let method = '';
|
||||
if ($scope.schedule) {
|
||||
if (AuthService.isAuthorized(['admin', 'manager']) && $rootScope.currentUser.id !== cartItems.customer_id) {
|
||||
method = $scope.method.payment_method;
|
||||
} else {
|
||||
method = 'card';
|
||||
}
|
||||
}
|
||||
if ($scope.amount > 0) {
|
||||
return _t('app.shared.cart.confirm_payment_of_html', { METHOD: method, AMOUNT: $filter('currency')($scope.amount) });
|
||||
} else {
|
||||
if ((price.price > 0) && ($scope.wallet.amount === 0)) {
|
||||
return _t('app.shared.cart.confirm_payment_of_html', { METHOD: method, AMOUNT: $filter('currency')(price.price) });
|
||||
} else {
|
||||
return _t('app.shared.buttons.confirm');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// # !!! MUST BE CALLED AT THE END of the controller
|
||||
initialize();
|
||||
}
|
||||
]
|
||||
}).result.finally(null).then(function (paymentSchedule) { afterPayment(paymentSchedule); });
|
||||
$scope.toggleLocalPaymentModal(() => {
|
||||
$scope.localPayment.cartItems = mkCartItems(items);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,4 +1,28 @@
|
||||
.local-payment-modal {
|
||||
|
||||
.local-payment-form {
|
||||
.payment-schedule {
|
||||
display: flex;
|
||||
|
||||
.schedule-method,
|
||||
.full-schedule {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.schedule-method {
|
||||
p {
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.full-schedule {
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.local-modal-icons {
|
||||
text-align: center;
|
||||
|
||||
|
@ -209,3 +209,13 @@
|
||||
customer="user"
|
||||
schedule="schedule.payment_schedule"/>
|
||||
</div>
|
||||
|
||||
<div ng-if="localPayment.showModal">
|
||||
<local-payment-modal is-open="localPayment.showModal"
|
||||
toggle-modal="toggleLocalPaymentModal"
|
||||
after-success="afterLocalPaymentSuccess"
|
||||
cart="localPayment.cartItems"
|
||||
current-user="currentUser"
|
||||
customer="user"
|
||||
schedule="schedule.payment_schedule"/>
|
||||
</div>
|
||||
|
@ -1360,7 +1360,7 @@ en:
|
||||
category_deleted: "The category was successfully deleted"
|
||||
unable_to_delete: "Unable to delete the category: "
|
||||
local_payment:
|
||||
offline_payment: "Offline payment"
|
||||
offline_payment: "Payment on site"
|
||||
about_to_cash: "You're about to confirm the cashing by an external payment mean. Please do not click on the button below until you have fully cashed the requested payment."
|
||||
payment_method: "Payment method"
|
||||
method_card: "Online by card"
|
||||
|
Loading…
x
Reference in New Issue
Block a user