mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-18 07:52:23 +01:00
(quality) compute withdrawal instructions server side
This commit is contained in:
parent
185b4f1459
commit
0464aae23e
@ -25,6 +25,17 @@ class API::OrdersController < API::ApiController
|
|||||||
head :no_content
|
head :no_content
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def withdrawal_instructions
|
||||||
|
begin
|
||||||
|
order = Order.find(params[:id])
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
order = nil
|
||||||
|
end
|
||||||
|
authorize order || Order
|
||||||
|
|
||||||
|
render html: ::Orders::OrderService.withdrawal_instructions(order)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_order
|
def set_order
|
||||||
|
@ -18,4 +18,9 @@ export default class OrderAPI {
|
|||||||
const res: AxiosResponse<Order> = await apiClient.patch(`/api/orders/${order.id}`, { order: { state, note } });
|
const res: AxiosResponse<Order> = await apiClient.patch(`/api/orders/${order.id}`, { order: { state, note } });
|
||||||
return res?.data;
|
return res?.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async withdrawalInstructions (order?: Order): Promise<string> {
|
||||||
|
const res: AxiosResponse<string> = await apiClient.get(`/api/orders/${order?.id}/withdrawal_instructions`);
|
||||||
|
return res?.data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,8 @@ import noImage from '../../../../images/no_image.png';
|
|||||||
import Switch from 'react-switch';
|
import Switch from 'react-switch';
|
||||||
import OrderLib from '../../lib/order';
|
import OrderLib from '../../lib/order';
|
||||||
import { CaretDown, CaretUp } from 'phosphor-react';
|
import { CaretDown, CaretUp } from 'phosphor-react';
|
||||||
import SettingAPI from '../../api/setting';
|
|
||||||
import { SettingName } from '../../models/setting';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import OrderAPI from '../../api/order';
|
||||||
|
|
||||||
declare const Application: IApplication;
|
declare const Application: IApplication;
|
||||||
|
|
||||||
@ -41,11 +40,11 @@ const StoreCart: React.FC<StoreCartProps> = ({ onSuccess, onError, currentUser,
|
|||||||
const [cartErrors, setCartErrors] = useState<OrderErrors>(null);
|
const [cartErrors, setCartErrors] = useState<OrderErrors>(null);
|
||||||
const [noMemberError, setNoMemberError] = useState<boolean>(false);
|
const [noMemberError, setNoMemberError] = useState<boolean>(false);
|
||||||
const [paymentModal, setPaymentModal] = useState<boolean>(false);
|
const [paymentModal, setPaymentModal] = useState<boolean>(false);
|
||||||
const [settings, setSettings] = useState<Map<SettingName, string>>(null);
|
const [withdrawalInstructions, setWithdrawalInstructions] = useState<string>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
SettingAPI.query(['store_withdrawal_instructions', 'fablab_name'])
|
OrderAPI.withdrawalInstructions(cart)
|
||||||
.then(res => setSettings(res))
|
.then(setWithdrawalInstructions)
|
||||||
.catch(onError);
|
.catch(onError);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -244,17 +243,6 @@ const StoreCart: React.FC<StoreCartProps> = ({ onSuccess, onError, currentUser,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Text instructions for the customer
|
|
||||||
*/
|
|
||||||
const withdrawalInstructions = (): string => {
|
|
||||||
const instructions = settings?.get('store_withdrawal_instructions');
|
|
||||||
if (!_.isEmpty(instructions)) {
|
|
||||||
return instructions;
|
|
||||||
}
|
|
||||||
return t('app.public.store_cart.please_contact_FABLAB', { FABLAB: settings?.get('fablab_name') });
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='store-cart'>
|
<div className='store-cart'>
|
||||||
<div className="store-cart-list">
|
<div className="store-cart-list">
|
||||||
@ -319,7 +307,7 @@ const StoreCart: React.FC<StoreCartProps> = ({ onSuccess, onError, currentUser,
|
|||||||
<div className="group">
|
<div className="group">
|
||||||
<div className='store-cart-info'>
|
<div className='store-cart-info'>
|
||||||
<h3>{t('app.public.store_cart.pickup')}</h3>
|
<h3>{t('app.public.store_cart.pickup')}</h3>
|
||||||
<p dangerouslySetInnerHTML={{ __html: withdrawalInstructions() }} />
|
<p dangerouslySetInnerHTML={{ __html: withdrawalInstructions }} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{cart && !cartIsEmpty() &&
|
{cart && !cartIsEmpty() &&
|
||||||
|
@ -50,13 +50,13 @@ export const OrderItem: React.FC<OrderItemProps> = ({ order, currentUser }) => {
|
|||||||
<span>{t('app.shared.store.order_item.created_at')}</span>
|
<span>{t('app.shared.store.order_item.created_at')}</span>
|
||||||
<div>
|
<div>
|
||||||
<p>{FormatLib.date(order.created_at)}
|
<p>{FormatLib.date(order.created_at)}
|
||||||
<div className="fab-tooltip">
|
<span className="fab-tooltip">
|
||||||
<span className="trigger"><PlusCircle size={16} weight="light" /></span>
|
<span className="trigger"><PlusCircle size={16} weight="light" /></span>
|
||||||
<div className="content">
|
<span className="content">
|
||||||
{t('app.shared.store.order_item.last_update')}<br />
|
{t('app.shared.store.order_item.last_update')}<br />
|
||||||
{FormatLib.date(order.updated_at)}
|
{FormatLib.date(order.updated_at)}
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import _ from 'lodash';
|
|
||||||
import { IApplication } from '../../models/application';
|
import { IApplication } from '../../models/application';
|
||||||
import { User } from '../../models/user';
|
import { User } from '../../models/user';
|
||||||
import { react2angular } from 'react2angular';
|
import { react2angular } from 'react2angular';
|
||||||
@ -12,8 +11,6 @@ import { Order } from '../../models/order';
|
|||||||
import FormatLib from '../../lib/format';
|
import FormatLib from '../../lib/format';
|
||||||
import OrderLib from '../../lib/order';
|
import OrderLib from '../../lib/order';
|
||||||
import { OrderActions } from './order-actions';
|
import { OrderActions } from './order-actions';
|
||||||
import SettingAPI from '../../api/setting';
|
|
||||||
import { SettingName } from '../../models/setting';
|
|
||||||
|
|
||||||
declare const Application: IApplication;
|
declare const Application: IApplication;
|
||||||
|
|
||||||
@ -31,17 +28,20 @@ export const ShowOrder: React.FC<ShowOrderProps> = ({ orderId, currentUser, onSu
|
|||||||
const { t } = useTranslation('shared');
|
const { t } = useTranslation('shared');
|
||||||
|
|
||||||
const [order, setOrder] = useState<Order>();
|
const [order, setOrder] = useState<Order>();
|
||||||
const [settings, setSettings] = useState<Map<SettingName, string>>(null);
|
const [withdrawalInstructions, setWithdrawalInstructions] = useState<string>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
OrderAPI.get(orderId).then(data => {
|
OrderAPI.get(orderId).then(data => {
|
||||||
setOrder(data);
|
setOrder(data);
|
||||||
}).catch(onError);
|
}).catch(onError);
|
||||||
SettingAPI.query(['store_withdrawal_instructions', 'fablab_name'])
|
|
||||||
.then(res => setSettings(res))
|
|
||||||
.catch(onError);
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
OrderAPI.withdrawalInstructions(order)
|
||||||
|
.then(setWithdrawalInstructions)
|
||||||
|
.catch(onError);
|
||||||
|
}, [order]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the current operator has administrative rights or is a normal member
|
* Check if the current operator has administrative rights or is a normal member
|
||||||
*/
|
*/
|
||||||
@ -79,17 +79,6 @@ export const ShowOrder: React.FC<ShowOrderProps> = ({ orderId, currentUser, onSu
|
|||||||
return paymentVerbose;
|
return paymentVerbose;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Text instructions for the customer
|
|
||||||
*/
|
|
||||||
const withdrawalInstructions = (): string => {
|
|
||||||
const instructions = settings?.get('store_withdrawal_instructions');
|
|
||||||
if (!_.isEmpty(instructions)) {
|
|
||||||
return instructions;
|
|
||||||
}
|
|
||||||
return t('app.shared.store.show_order.please_contact_FABLAB', { FABLAB: settings?.get('fablab_name') });
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback after action success
|
* Callback after action success
|
||||||
*/
|
*/
|
||||||
@ -202,7 +191,7 @@ export const ShowOrder: React.FC<ShowOrderProps> = ({ orderId, currentUser, onSu
|
|||||||
</div>
|
</div>
|
||||||
<div className="withdrawal-instructions">
|
<div className="withdrawal-instructions">
|
||||||
<label>{t('app.shared.store.show_order.pickup')}</label>
|
<label>{t('app.shared.store.show_order.pickup')}</label>
|
||||||
<p dangerouslySetInnerHTML={{ __html: withdrawalInstructions() }} />
|
<p dangerouslySetInnerHTML={{ __html: withdrawalInstructions }} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,4 +13,8 @@ class OrderPolicy < ApplicationPolicy
|
|||||||
def destroy?
|
def destroy?
|
||||||
user.privileged?
|
user.privileged?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def withdrawal_instructions?
|
||||||
|
user.privileged? || (record&.statistic_profile_id == user.statistic_profile.id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
# Provides methods for Order
|
# Provides methods for Order
|
||||||
class Orders::OrderService
|
class Orders::OrderService
|
||||||
class << self
|
class << self
|
||||||
|
include ApplicationHelper
|
||||||
|
|
||||||
ORDERS_PER_PAGE = 20
|
ORDERS_PER_PAGE = 20
|
||||||
|
|
||||||
def list(filters, current_user)
|
def list(filters, current_user)
|
||||||
@ -71,6 +73,14 @@ class Orders::OrderService
|
|||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def withdrawal_instructions(order)
|
||||||
|
res = order&.order_activities&.find_by(activity_type: 'ready')&.note.presence ||
|
||||||
|
Setting.get('store_withdrawal_instructions').presence ||
|
||||||
|
_t('order.please_contact_FABLAB', FABLAB: Setting.get('fablab_name').presence || 'empty')
|
||||||
|
|
||||||
|
ActionController::Base.helpers.sanitize(res, tags: %w[p ul li h3 u em strong a], attributes: %w[target rel href])
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def filter_by_user(orders, filters, current_user)
|
def filter_by_user(orders, filters, current_user)
|
||||||
|
@ -4,5 +4,5 @@
|
|||||||
<%= t('.body.notify_user_order_is_ready', REFERENCE: @attached_object.order.reference) %>
|
<%= t('.body.notify_user_order_is_ready', REFERENCE: @attached_object.order.reference) %>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<%= sanitize @attached_object.note.presence || Setting.get('store_withdrawal_istructions').presence || _t('notify_user_order_is_ready.body.please_contact_FABLAB', FABLAB: Setting.get('fablab_name')) %>
|
<%= ::Orders::OrderService.withdrawal_instructions(@attached_object.order) %>
|
||||||
</p>
|
</p>
|
||||||
|
@ -442,7 +442,6 @@ en:
|
|||||||
checkout_error: "An unexpected error occurred. Please contact the administrator."
|
checkout_error: "An unexpected error occurred. Please contact the administrator."
|
||||||
checkout_success: "Purchase confirmed. Thanks!"
|
checkout_success: "Purchase confirmed. Thanks!"
|
||||||
select_user: "Please select a user before continuing."
|
select_user: "Please select a user before continuing."
|
||||||
please_contact_FABLAB: "Please contact {FABLAB, select, undefined{us} other{{FABLAB}}} for withdrawal instructions."
|
|
||||||
update_item: "Update"
|
update_item: "Update"
|
||||||
errors:
|
errors:
|
||||||
product_not_found: "This product is no longer available, please remove it from your cart."
|
product_not_found: "This product is no longer available, please remove it from your cart."
|
||||||
|
@ -593,7 +593,6 @@ en:
|
|||||||
coupon: "Coupon"
|
coupon: "Coupon"
|
||||||
cart_total: "Cart total"
|
cart_total: "Cart total"
|
||||||
pickup: "Pickup your products"
|
pickup: "Pickup your products"
|
||||||
please_contact_FABLAB: "Please contact {FABLAB, select, undefined{us} other{{FABLAB}}} for withdrawal instructions."
|
|
||||||
state:
|
state:
|
||||||
cart: 'Cart'
|
cart: 'Cart'
|
||||||
in_progress: 'Under preparation'
|
in_progress: 'Under preparation'
|
||||||
@ -623,12 +622,12 @@ en:
|
|||||||
delivered: "Delivered"
|
delivered: "Delivered"
|
||||||
confirm: 'Confirm'
|
confirm: 'Confirm'
|
||||||
confirmation_required: "Confirmation required"
|
confirmation_required: "Confirmation required"
|
||||||
confirm_order_in_progress_html: "Please confirm this order in being prepared."
|
confirm_order_in_progress_html: "Please confirm that this order in being prepared."
|
||||||
order_in_progress_success: "Order is under preparation"
|
order_in_progress_success: "Order is under preparation"
|
||||||
confirm_order_ready_html: "Please confirm this order is ready."
|
confirm_order_ready_html: "Please confirm that this order is ready."
|
||||||
order_ready_note: 'You can leave a message to the customer about withdrawal instructions'
|
order_ready_note: 'You can leave a message to the customer about withdrawal instructions'
|
||||||
order_ready_success: "Order is ready"
|
order_ready_success: "Order is ready"
|
||||||
confirm_order_delivered_html: "Please confirm this order was delivered."
|
confirm_order_delivered_html: "Please confirm that this order was delivered."
|
||||||
order_delivered_success: "Order was delivered"
|
order_delivered_success: "Order was delivered"
|
||||||
confirm_order_canceled_html: "<strong>Do you really want to cancel this order?</strong><p>If this impacts stock, please reflect the change in <em>edit product > stock management</em>. This won't be automatic.</p>"
|
confirm_order_canceled_html: "<strong>Do you really want to cancel this order?</strong><p>If this impacts stock, please reflect the change in <em>edit product > stock management</em>. This won't be automatic.</p>"
|
||||||
order_canceled_success: "Order was canceled"
|
order_canceled_success: "Order was canceled"
|
||||||
|
@ -44,8 +44,8 @@ en:
|
|||||||
registration_disabled: "Registration is disabled"
|
registration_disabled: "Registration is disabled"
|
||||||
undefined_in_store: "must be defined to make the product available in the store"
|
undefined_in_store: "must be defined to make the product available in the store"
|
||||||
gateway_error: "Payement gateway error: %{MESSAGE}"
|
gateway_error: "Payement gateway error: %{MESSAGE}"
|
||||||
gateway_amount_too_small: "Payments under %{AMOUNT} are not supported. Please contact reception directly."
|
gateway_amount_too_small: "Payments under %{AMOUNT} are not supported. Please order directly at the reception."
|
||||||
gateway_amount_too_large: "Payments above %{AMOUNT} are not supported. Please contact reception directly."
|
gateway_amount_too_large: "Payments above %{AMOUNT} are not supported. Please order directly at the reception."
|
||||||
apipie:
|
apipie:
|
||||||
api_documentation: "API Documentation"
|
api_documentation: "API Documentation"
|
||||||
code: "HTTP code"
|
code: "HTTP code"
|
||||||
@ -475,6 +475,8 @@ en:
|
|||||||
free_extension: "Free extension of a subscription, until %{DATE}"
|
free_extension: "Free extension of a subscription, until %{DATE}"
|
||||||
statistic_profile:
|
statistic_profile:
|
||||||
birthday_in_past: "The date of birth must be in the past"
|
birthday_in_past: "The date of birth must be in the past"
|
||||||
|
order:
|
||||||
|
please_contact_FABLAB: "Please contact {FABLAB, select, nil{us} other{{FABLAB}}} for withdrawal instructions."
|
||||||
settings:
|
settings:
|
||||||
locked_setting: "the setting is locked."
|
locked_setting: "the setting is locked."
|
||||||
about_title: "\"About\" page title"
|
about_title: "\"About\" page title"
|
||||||
|
@ -378,12 +378,11 @@ en:
|
|||||||
subject: "Your command is ready"
|
subject: "Your command is ready"
|
||||||
body:
|
body:
|
||||||
notify_user_order_is_ready: "Your command %{REFERENCE} is ready:"
|
notify_user_order_is_ready: "Your command %{REFERENCE} is ready:"
|
||||||
please_contact_FABLAB: "Please contact {FABLAB, select, undefined{us} other{{FABLAB}}} for withdrawal instructions."
|
|
||||||
notify_user_order_is_canceled:
|
notify_user_order_is_canceled:
|
||||||
subject: "Your command is canceled"
|
subject: "Your command was canceled"
|
||||||
body:
|
body:
|
||||||
notify_user_order_is_canceled: "Your command %{REFERENCE} is canceled."
|
notify_user_order_is_canceled: "Your command %{REFERENCE} was canceled."
|
||||||
notify_user_order_is_refunded:
|
notify_user_order_is_refunded:
|
||||||
subject: "Your command is refunded"
|
subject: "Your command was refunded"
|
||||||
body:
|
body:
|
||||||
notify_user_order_is_refunded: "Your command %{REFERENCE} is refunded:"
|
notify_user_order_is_refunded: "Your command %{REFERENCE} was refunded."
|
||||||
|
@ -169,7 +169,9 @@ Rails.application.routes.draw do
|
|||||||
post 'payment', on: :collection
|
post 'payment', on: :collection
|
||||||
post 'confirm_payment', on: :collection
|
post 'confirm_payment', on: :collection
|
||||||
end
|
end
|
||||||
resources :orders, except: %i[create]
|
resources :orders, except: %i[create] do
|
||||||
|
get 'withdrawal_instructions', on: :member
|
||||||
|
end
|
||||||
|
|
||||||
# for admin
|
# for admin
|
||||||
resources :trainings do
|
resources :trainings do
|
||||||
|
Loading…
x
Reference in New Issue
Block a user