diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index 028544a33be..6ae66174f50 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -52,7 +52,7 @@ class PosOrder(models.Model): 'amount_return': ui_order['amount_return'], 'company_id': self.env['pos.session'].browse(ui_order['pos_session_id']).company_id.id, 'to_invoice': ui_order['to_invoice'] if "to_invoice" in ui_order else False, - 'to_ship': ui_order['to_ship'] if "to_ship" in ui_order else False, + 'shipping_date': ui_order['shipping_date'] if "shipping_date" in ui_order else False, 'is_tipped': ui_order.get('is_tipped', False), 'tip_amount': ui_order.get('tip_amount', 0), 'access_token': ui_order.get('access_token', '') @@ -284,7 +284,7 @@ class PosOrder(models.Model): payment_ids = fields.One2many('pos.payment', 'pos_order_id', string='Payments', readonly=True) session_move_id = fields.Many2one('account.move', string='Session Journal Entry', related='session_id.move_id', readonly=True, copy=False) to_invoice = fields.Boolean('To invoice', copy=False) - to_ship = fields.Boolean('To ship') + shipping_date = fields.Date('Shipping Date') is_invoiced = fields.Boolean('Is Invoiced', compute='_compute_is_invoiced') is_tipped = fields.Boolean('Is this already tipped?', readonly=True) tip_amount = fields.Float(string='Tip Amount', digits=0, readonly=True) @@ -887,7 +887,7 @@ class PosOrder(models.Model): def _create_order_picking(self): self.ensure_one() - if self.to_ship: + if self.shipping_date: self.lines._launch_stock_rule_from_pos_order_lines() else: if self._should_create_picking_real_time(): @@ -1028,7 +1028,7 @@ class PosOrder(models.Model): 'creation_date': order.date_order.astimezone(timezone), 'fiscal_position_id': order.fiscal_position_id.id, 'to_invoice': order.to_invoice, - 'to_ship': order.to_ship, + 'shipping_date': order.shipping_date, 'state': order.state, 'account_move': order.account_move.id, 'id': order.id, @@ -1238,7 +1238,11 @@ class PosOrderLine(models.Model): """ self.ensure_one() # Use the delivery date if there is else use date_order and lead time - date_deadline = self.order_id.date_order + if self.order_id.shipping_date: + date_deadline = self.order_id.shipping_date + else: + date_deadline = self.order_id.date_order + values = { 'group_id': group_id, 'date_planned': date_deadline, diff --git a/addons/point_of_sale/models/pos_session.py b/addons/point_of_sale/models/pos_session.py index afe5bbf94d2..c8240e69846 100644 --- a/addons/point_of_sale/models/pos_session.py +++ b/addons/point_of_sale/models/pos_session.py @@ -587,7 +587,7 @@ class PosSession(models.Model): session_destination_id = picking_type.default_location_dest_id.id for order in self.order_ids: - if order.company_id.anglo_saxon_accounting and order.is_invoiced or order.to_ship: + if order.company_id.anglo_saxon_accounting and order.is_invoiced or order.shipping_date: continue destination_id = order.partner_id.property_stock_customer.id or session_destination_id if destination_id in lines_grouped_by_dest_location: diff --git a/addons/point_of_sale/static/src/js/Popups/DatePickerPopup.js b/addons/point_of_sale/static/src/js/Popups/DatePickerPopup.js new file mode 100644 index 00000000000..e7a7864a277 --- /dev/null +++ b/addons/point_of_sale/static/src/js/Popups/DatePickerPopup.js @@ -0,0 +1,33 @@ +/** @odoo-module */ + +import AbstractAwaitablePopup from "@point_of_sale/js/Popups/AbstractAwaitablePopup"; +import Registries from "@point_of_sale/js/Registries"; +import { _lt } from "@web/core/l10n/translation"; + +const { onMounted, useRef, useState } = owl; + +class DatePickerPopup extends AbstractAwaitablePopup { + setup() { + super.setup(); + this.state = useState({shippingDate: this._today()}); + this.inputRef = useRef('input'); + onMounted(() => this.inputRef.el.focus()); + } + getPayload() { + return this.state.shippingDate < this._today() ? this._today(): this.state.shippingDate; + } + _today() { + return new Date().toISOString().split('T')[0] + } + +} +DatePickerPopup.template = 'DatePickerPopup'; +DatePickerPopup.defaultProps = { + confirmText: _lt('Confirm'), + cancelText: _lt('Discard'), + title: _lt('DatePicker'), +}; + +Registries.Component.add(DatePickerPopup); + +export default DatePickerPopup; diff --git a/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreen.js b/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreen.js index e202fc38061..9eac2d4006c 100644 --- a/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreen.js +++ b/addons/point_of_sale/static/src/js/Screens/PaymentScreen/PaymentScreen.js @@ -138,10 +138,17 @@ class PaymentScreen extends PosComponent { this.currentOrder.set_tip(parse.float(payload)); } } - toggleIsToShip() { - // click_ship - this.currentOrder.set_to_ship(!this.currentOrder.is_to_ship()); - this.render(true); + async toggleShippingDatePicker() { + if (!this.currentOrder.getShippingDate()) { + const { confirmed, payload: shippingDate } = await this.showPopup("DatePickerPopup", { + title: this.env._t("Select the shipping date"), + }); + if (confirmed) { + this.currentOrder.setShippingDate(shippingDate); + } + } else { + this.currentOrder.setShippingDate(false); + } } deletePaymentLine(event) { var self = this; @@ -326,7 +333,7 @@ class PaymentScreen extends PosComponent { } if ( - (this.currentOrder.is_to_invoice() || this.currentOrder.is_to_ship()) && + (this.currentOrder.is_to_invoice() || this.currentOrder.getShippingDate()) && !this.currentOrder.get_partner() ) { const { confirmed } = await this.showPopup("ConfirmPopup", { @@ -343,7 +350,7 @@ class PaymentScreen extends PosComponent { const partner = this.currentOrder.get_partner(); if ( - this.currentOrder.is_to_ship() && + this.currentOrder.getShippingDate() && !(partner.name && partner.street && partner.city && partner.country_id) ) { this.showPopup("ErrorPopup", { diff --git a/addons/point_of_sale/static/src/js/Screens/ReceiptScreen/OrderReceipt.js b/addons/point_of_sale/static/src/js/Screens/ReceiptScreen/OrderReceipt.js index 4f7b5e29f55..8de7f185c5f 100644 --- a/addons/point_of_sale/static/src/js/Screens/ReceiptScreen/OrderReceipt.js +++ b/addons/point_of_sale/static/src/js/Screens/ReceiptScreen/OrderReceipt.js @@ -29,6 +29,9 @@ class OrderReceipt extends PosComponent { get receiptEnv() { return this._receiptEnv; } + get shippingDate() { + return this.receiptEnv.shippingDate + } isSimple(line) { return ( line.discount === 0 && diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 7ca30c86f32..942bf4c4e37 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -2496,6 +2496,7 @@ export class Order extends PosModel { this.cashier = this.pos.get_cashier(); this.finalized = false; // if true, cannot be modified. this.set_pricelist(this.pos.default_pricelist); + this.shippingDate = null; this.partner = null; @@ -2591,9 +2592,9 @@ export class Order extends PosModel { } this.set_partner(partner); - this.temporary = false; // FIXME - this.to_invoice = false; // FIXME - this.to_ship = false; + this.temporary = false; // FIXME + this.to_invoice = false; // FIXME + this.shippingDate = json.shipping_date; var orderlines = json.lines; for (var i = 0; i < orderlines.length; i++) { @@ -2659,7 +2660,7 @@ export class Order extends PosModel { fiscal_position_id: this.fiscal_position ? this.fiscal_position.id : false, server_id: this.server_id ? this.server_id : false, to_invoice: this.to_invoice ? this.to_invoice : false, - to_ship: this.to_ship ? this.to_ship : false, + shipping_date: this.shippingDate ? this.shippingDate : false, is_tipped: this.is_tipped || false, tip_amount: this.tip_amount || 0, access_token: this.access_token || "", @@ -2669,6 +2670,16 @@ export class Order extends PosModel { } return json; } + _exportShippingDateForPrinting() { + const shippingDate = new Date(this.shippingDate); + const localeShippingDate = field_utils.format.date( + moment(shippingDate), {}, {timezone: false}); + const exportedDate = { + localestring: localeShippingDate, + validationDate: shippingDate, + }; + return exportedDate; + } export_for_printing() { var orderlines = []; var self = this; @@ -3536,6 +3547,7 @@ export class Order extends PosModel { receipt: this.export_for_printing(), orderlines: this.get_orderlines(), paymentlines: this.get_paymentlines(), + shippingDate: this.shippingDate ? this._exportShippingDateForPrinting() : false, }; } updatePricelist(newPartner) { @@ -3561,12 +3573,11 @@ export class Order extends PosModel { this.set_pricelist(newPartnerPricelist); } /* ---- Ship later --- */ - set_to_ship(to_ship) { - this.assert_editable(); - this.to_ship = to_ship; + setShippingDate(shippingDate) { + this.shippingDate = shippingDate } - is_to_ship() { - return this.to_ship; + getShippingDate() { + return this.shippingDate; } getHasRefundLines() { for (const line of this.get_orderlines()) { diff --git a/addons/point_of_sale/static/src/xml/Popups/DatePickerPopup.xml b/addons/point_of_sale/static/src/xml/Popups/DatePickerPopup.xml new file mode 100644 index 00000000000..121bf230d1a --- /dev/null +++ b/addons/point_of_sale/static/src/xml/Popups/DatePickerPopup.xml @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentScreen.xml b/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentScreen.xml index 8411e587d3a..fcbe8512d0f 100644 --- a/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentScreen.xml +++ b/addons/point_of_sale/static/src/xml/Screens/PaymentScreen/PaymentScreen.xml @@ -90,9 +90,12 @@ Open Cashbox
+ t-att-class="{ highlight: currentOrder.getShippingDate() }" + t-on-click="toggleShippingDatePicker"> Ship Later + + () +
diff --git a/addons/point_of_sale/static/src/xml/Screens/ReceiptScreen/OrderReceipt.xml b/addons/point_of_sale/static/src/xml/Screens/ReceiptScreen/OrderReceipt.xml index 1143af2db90..888da5fb2aa 100644 --- a/addons/point_of_sale/static/src/xml/Screens/ReceiptScreen/OrderReceipt.xml +++ b/addons/point_of_sale/static/src/xml/Screens/ReceiptScreen/OrderReceipt.xml @@ -153,6 +153,19 @@ +
+ +
+ Expected delivery: + +
+
+ +
+
+
+
+
diff --git a/addons/point_of_sale/tests/test_point_of_sale_flow.py b/addons/point_of_sale/tests/test_point_of_sale_flow.py index 28a86e2c71d..0730d3d8e76 100644 --- a/addons/point_of_sale/tests/test_point_of_sale_flow.py +++ b/addons/point_of_sale/tests/test_point_of_sale_flow.py @@ -1040,7 +1040,7 @@ class TestPointOfSaleFlow(TestPointOfSaleCommon): 'amount_total': untax+tax, 'amount_paid': 0, 'amount_return': 0, - 'to_ship': True, + 'shipping_date': fields.Date.today(), }) context_make_payment = { diff --git a/addons/pos_sale/static/src/js/OrderManagementScreen/SaleOrderManagementScreen.js b/addons/pos_sale/static/src/js/OrderManagementScreen/SaleOrderManagementScreen.js index 54f4e8fc8db..a207b0b254b 100644 --- a/addons/pos_sale/static/src/js/OrderManagementScreen/SaleOrderManagementScreen.js +++ b/addons/pos_sale/static/src/js/OrderManagementScreen/SaleOrderManagementScreen.js @@ -86,6 +86,7 @@ class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentToOrderSc if (confirmed) { const currentPOSOrder = this.env.pos.get_order(); const sale_order = await this._getSaleOrder(clickedOrder.id); + clickedOrder.shipping_date = sale_order.shipping_date try { await this.env.pos.load_new_partners(); } catch { @@ -293,6 +294,7 @@ class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentToOrderSc "fiscal_position_id", "amount_total", "amount_untaxed", + "picking_ids", ], ], context: this.env.session.user_context, @@ -300,6 +302,16 @@ class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentToOrderSc const sale_lines = await this._getSOLines(sale_order[0].order_line); sale_order[0].order_line = sale_lines; + + const picking_id = await this.rpc({ + model: 'stock.picking', + method: 'read', + args: [ + [sale_order[0].picking_ids[0]], + ['scheduled_date'], + ], + }); + sale_order[0].shipping_date = picking_id[0].scheduled_date; return sale_order[0]; } diff --git a/addons/pos_sale/static/src/js/models.js b/addons/pos_sale/static/src/js/models.js index 0bec1f6ab73..f843ce530df 100644 --- a/addons/pos_sale/static/src/js/models.js +++ b/addons/pos_sale/static/src/js/models.js @@ -33,6 +33,9 @@ const PosSaleOrderline = (Orderline) => this.sale_order_line_id = this.sale_order_line_id || options.sale_order_line_id; this.down_payment_details = this.down_payment_details || options.down_payment_details; this.customerNote = this.customerNote || options.customer_note; + if (this.sale_order_origin_id && this.sale_order_origin_id.shipping_date) { + this.order.setShippingDate(this.sale_order_origin_id.shipping_date); + } } init_from_JSON(json) { super.init_from_JSON(...arguments);