[FIX] pos_sale: correctly assign partners in the invoice

When settling a sale.order from the pos app, it's possible that the sale.order
have different partners assigned to it (partner_id, partner_shipping_id,
partner_invoice_id). After this commit, we are now properly assigning these
partners to the generated invoice.

Additionally, this commit makes the settling of sale.order more robust by
avoiding issue on having multiple incompatible sale.orders in a pos.order (more
than 2 sale.orders and their partners are not the same). After this commit, we
are only reusing the current pos.order as container of the new sale.order to
settle if the sale.order is compatible to the current pos.order. Basically, we
now only allow adding new sale order to the current order if the partner_id,
partner_invoice_id and partner_shipping_id of the new sale.order are the same to
the current pos.order's linked sale.order. In other words, if the new sale.order
to settle is incompatible to the current pos.order, we create a new pos.order to
settle that sale.order.

closes odoo/odoo#120649

X-original-commit: 2782bedafb9fcbe7c65f4c6951ce270c3a5a5bd3
Signed-off-by: Heinz Robin (rhe) <rhe@odoo.com>
Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
This commit is contained in:
rhe-odoo
2023-05-05 14:55:05 +02:00
parent bc2d796dbc
commit d28363ba5b
5 changed files with 110 additions and 8 deletions
+10 -7
View File
@@ -32,13 +32,16 @@ class PosOrder(models.Model):
invoice_vals = super(PosOrder, self)._prepare_invoice_vals()
invoice_vals['team_id'] = self.crm_team_id.id
sale_orders = self.lines.mapped('sale_order_origin_id')
if sale_orders and (sale_orders.partner_invoice_id.id != sale_orders.partner_shipping_id.id):
invoice_vals['partner_shipping_id'] = sale_orders.partner_shipping_id.id
else:
addr = self.partner_id.address_get(['delivery'])
invoice_vals['partner_shipping_id'] = addr['delivery']
if sale_orders and sale_orders[0].payment_term_id:
invoice_vals['invoice_payment_term_id'] = sale_orders[0].payment_term_id.id,
if sale_orders:
if sale_orders[0].partner_invoice_id.id != sale_orders[0].partner_shipping_id.id:
invoice_vals['partner_shipping_id'] = sale_orders[0].partner_shipping_id.id
else:
addr = self.partner_id.address_get(['delivery'])
invoice_vals['partner_shipping_id'] = addr['delivery']
if sale_orders[0].payment_term_id:
invoice_vals['invoice_payment_term_id'] = sale_orders[0].payment_term_id.id
if sale_orders[0].partner_invoice_id != sale_orders[0].partner_id:
invoice_vals['partner_id'] = sale_orders[0].partner_invoice_id.id
return invoice_vals
@api.model
@@ -18,6 +18,15 @@ import { SaleOrderList } from "./SaleOrderList";
import { SaleOrderManagementControlPanel } from "./SaleOrderManagementControlPanel";
import { onMounted, useRef, useState } from "@odoo/owl";
/**
* ID getter to take into account falsy many2one value.
* @param {[id: number, display_name: string] | false} fieldVal many2one field value
* @returns {number | false}
*/
function getId(fieldVal) {
return fieldVal && fieldVal[0];
}
export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentToOrderScreen) {
static components = { SaleOrderList, SaleOrderManagementControlPanel };
static template = "SaleOrderManagementScreen";
@@ -29,6 +38,7 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
this.root = useRef("root");
this.numberBuffer = useService("number_buffer");
this.saleOrderFetcher = useService("sale_order_fetcher");
this.notification = useService("pos_notification");
useBus(this.saleOrderFetcher, "update", this.render);
this.orderManagementContext = useState(orderManagement);
@@ -48,6 +58,14 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
);
this.saleOrderFetcher.setNPerPage(val);
this.saleOrderFetcher.fetch();
}
_getSaleOrderOrigin(order) {
for (const line of order.get_orderlines()) {
if (line.sale_order_origin_id) {
return line.sale_order_origin_id
}
}
return false;
}
get selectedPartner() {
const order = this.orderManagementContext.selectedOrder;
@@ -82,9 +100,25 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
});
if (confirmed) {
const currentPOSOrder = this.env.pos.get_order();
let currentPOSOrder = this.env.pos.get_order();
const sale_order = await this._getSaleOrder(clickedOrder.id);
clickedOrder.shipping_date = sale_order.shipping_date;
const currentSaleOrigin = this._getSaleOrderOrigin(currentPOSOrder);
const currentSaleOriginId = currentSaleOrigin && currentSaleOrigin.id;
if (currentSaleOriginId) {
const linkedSO = await this._getSaleOrder(currentSaleOriginId);
if (
getId(linkedSO.partner_id) !== getId(sale_order.partner_id) ||
getId(linkedSO.partner_invoice_id) !== getId(sale_order.partner_invoice_id) ||
getId(linkedSO.partner_shipping_id) !== getId(sale_order.partner_shipping_id)
) {
currentPOSOrder = this.env.pos.add_new_order();
this.notification.add(this.env._t("A new order has been created."), 4000);
}
}
try {
await this.env.pos.load_new_partners();
} catch {
@@ -318,6 +352,8 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(IndependentTo
"amount_untaxed",
"amount_unpaid",
"picking_ids",
"partner_shipping_id",
"partner_invoice_id"
]
);
@@ -25,6 +25,18 @@ class DoExt extends Do {
}
];
}
selectNthOrder(n) {
return [
{
content: `select order`,
trigger: `.order-list .order-row:nth-child(${n})`,
},
{
content: `click on select the order`,
trigger: `.selection-item:contains('Settle the order')`,
}
];
}
}
class CheckExt extends Check {}
@@ -25,3 +25,22 @@ Chrome.do.clickMenuButton();
Chrome.do.clickTicketButton();
registry.category("web_tour.tours").add('PosSettleOrder', { test: true, url: '/pos/ui', steps: getSteps() });
startSteps();
ProductScreen.do.confirmOpeningPopup();
ProductScreen.do.clickQuotationButton();
// The second item in the list is the first sale.order.
ProductScreen.do.selectNthOrder(2);
ProductScreen.check.selectedOrderlineHas('product1', 1);
ProductScreen.check.totalAmountIs("10.00");
ProductScreen.do.clickQuotationButton();
// The first item in the list is the second sale.order.
// Selecting the 2nd sale.order should use a new order,
// therefore, the total amount will change.
ProductScreen.do.selectNthOrder(1);
ProductScreen.check.selectedOrderlineHas('product2', 1);
ProductScreen.check.totalAmountIs("11.00");
registry.category("web_tour.tours").add('PosSettleOrderIncompatiblePartner', { test: true, url: '/pos/ui', steps: getSteps() });
@@ -70,3 +70,35 @@ class TestPoSSale(TestPointOfSaleHttpCommon):
self.assertEqual(sale_order.picking_ids[0].move_ids.quantity_done, 0)
self.assertEqual(sale_order.picking_ids[1].move_ids.product_qty, 300)
self.assertEqual(sale_order.picking_ids[1].move_ids.quantity_done, 300) # 1 delivered => 300 * 2 = 600
def test_settle_order_with_incompatible_partner(self):
""" If the partner of the sale order is not compatible with the current pos order,
then a new pos order should be to settle the newly selected sale order.
"""
product1 = self.env['product.product'].create({
'name': 'product1',
'available_in_pos': True,
'type': 'product',
'lst_price': 10,
'taxes_id': [odoo.Command.clear()],
})
product2 = self.env['product.product'].create({
'name': 'product2',
'available_in_pos': True,
'type': 'product',
'lst_price': 11,
'taxes_id': [odoo.Command.clear()],
})
self.env['sale.order'].create({
'partner_id': self.env.ref('base.res_partner_1').id,
'partner_shipping_id': self.env.ref('base.res_partner_2').id,
'order_line': [(0, 0, {'product_id': product1.id})],
})
self.env['sale.order'].create({
'partner_id': self.env.ref('base.res_partner_1').id,
'partner_shipping_id': self.env.ref('base.res_partner_1').id,
'order_line': [(0, 0, {'product_id': product2.id})],
})
self.main_pos_config.open_ui()
self.start_tour("/pos/ui?config_id=%d" % self.main_pos_config.id, 'PosSettleOrderIncompatiblePartner', login="accountman")