[IMP] rma, rma_sale: shipping address

Allow to set the desired shipping address where the goods must be
returned after the RMA is processed.
This commit is contained in:
david
2020-11-06 16:56:02 +01:00
parent a05cdcf1ce
commit f32c4ca97d
8 changed files with 118 additions and 15 deletions

View File

@@ -73,6 +73,13 @@ class Rma(models.Model):
index=True, index=True,
tracking=True, tracking=True,
) )
partner_shipping_id = fields.Many2one(
string="Shipping Address",
comodel_name="res.partner",
readonly=True,
states={"draft": [("readonly", False)]},
help="Shipping address for current RMA.",
)
partner_invoice_id = fields.Many2one( partner_invoice_id = fields.Many2one(
string="Invoice Address", string="Invoice Address",
comodel_name="res.partner", comodel_name="res.partner",
@@ -385,7 +392,9 @@ class Rma(models.Model):
record.access_url = "/my/rmas/{}".format(record.id) record.access_url = "/my/rmas/{}".format(record.id)
# Constrains methods (@api.constrains) # Constrains methods (@api.constrains)
@api.constrains("state", "partner_id", "partner_invoice_id", "product_id") @api.constrains(
"state", "partner_id", "partner_shipping_id", "partner_invoice_id", "product_id"
)
def _check_required_after_draft(self): def _check_required_after_draft(self):
""" Check that RMAs are being created or edited with the """ Check that RMAs are being created or edited with the
necessary fields filled out. Only applies to 'Draft' and necessary fields filled out. Only applies to 'Draft' and
@@ -420,10 +429,13 @@ class Rma(models.Model):
def _onchange_partner_id(self): def _onchange_partner_id(self):
self.picking_id = False self.picking_id = False
partner_invoice_id = False partner_invoice_id = False
partner_shipping_id = False
if self.partner_id: if self.partner_id:
address = self.partner_id.address_get(["invoice"]) address = self.partner_id.address_get(["invoice", "delivery"])
partner_invoice_id = address.get("invoice", False) partner_invoice_id = address.get("invoice", False)
partner_shipping_id = address.get("delivery", False)
self.partner_invoice_id = partner_invoice_id self.partner_invoice_id = partner_invoice_id
self.partner_shipping_id = partner_shipping_id
@api.onchange("picking_id") @api.onchange("picking_id")
def _onchange_picking_id(self): def _onchange_picking_id(self):
@@ -723,7 +735,10 @@ class Rma(models.Model):
# Validation business methods # Validation business methods
def _ensure_required_fields(self): def _ensure_required_fields(self):
""" This method is used to ensure the following fields are not empty: """ This method is used to ensure the following fields are not empty:
['partner_id', 'partner_invoice_id', 'product_id', 'location_id'] [
'partner_id', 'partner_invoice_id', 'partner_shipping_id',
'product_id', 'location_id'
]
This method is intended to be called on confirm RMA action and is This method is intended to be called on confirm RMA action and is
invoked by: invoked by:
@@ -731,7 +746,13 @@ class Rma(models.Model):
rma.action_confirm rma.action_confirm
""" """
ir_translation = self.env["ir.translation"] ir_translation = self.env["ir.translation"]
required = ["partner_id", "partner_invoice_id", "product_id", "location_id"] required = [
"partner_id",
"partner_shipping_id",
"partner_invoice_id",
"product_id",
"location_id",
]
for record in self: for record in self:
desc = "" desc = ""
for field in filter(lambda item: not record[item], required): for field in filter(lambda item: not record[item], required):
@@ -863,7 +884,7 @@ class Rma(models.Model):
def _prepare_picking(self, picking_form): def _prepare_picking(self, picking_form):
picking_form.origin = self.name picking_form.origin = self.name
picking_form.partner_id = self.partner_id picking_form.partner_id = self.partner_shipping_id
picking_form.location_dest_id = self.location_id picking_form.location_dest_id = self.location_id
with picking_form.move_ids_without_package.new() as move_form: with picking_form.move_ids_without_package.new() as move_form:
move_form.product_id = self.product_id move_form.product_id = self.product_id
@@ -962,7 +983,11 @@ class Rma(models.Model):
group_dict = {} group_dict = {}
rmas_to_return = self.filtered("can_be_returned") rmas_to_return = self.filtered("can_be_returned")
for record in rmas_to_return: for record in rmas_to_return:
key = (record.partner_id.id, record.company_id.id, record.warehouse_id) key = (
record.partner_shipping_id.id,
record.company_id.id,
record.warehouse_id,
)
group_dict.setdefault(key, self.env["rma"]) group_dict.setdefault(key, self.env["rma"])
group_dict[key] |= record group_dict[key] |= record
for rmas in group_dict.values(): for rmas in group_dict.values():
@@ -1010,7 +1035,7 @@ class Rma(models.Model):
def _prepare_returning_picking(self, picking_form, origin=None): def _prepare_returning_picking(self, picking_form, origin=None):
picking_form.picking_type_id = self.warehouse_id.rma_out_type_id picking_form.picking_type_id = self.warehouse_id.rma_out_type_id
picking_form.origin = origin or self.name picking_form.origin = origin or self.name
picking_form.partner_id = self.partner_id picking_form.partner_id = self.partner_shipping_id
def _prepare_returning_move( def _prepare_returning_move(
self, move_form, scheduled_date, quantity=None, uom=None self, move_form, scheduled_date, quantity=None, uom=None
@@ -1076,7 +1101,7 @@ class Rma(models.Model):
{ {
"name": self.name, "name": self.name,
"move_type": "direct", "move_type": "direct",
"partner_id": self.partner_id.id, "partner_id": self.partner_shipping_id.id,
} }
) )
.id .id
@@ -1088,7 +1113,7 @@ class Rma(models.Model):
product, product,
qty, qty,
uom, uom,
self.partner_id.property_stock_customer, self.partner_shipping_id.property_stock_customer,
self.product_id.display_name, self.product_id.display_name,
self.procurement_group_id.name, self.procurement_group_id.name,
self.company_id, self.company_id,
@@ -1106,7 +1131,7 @@ class Rma(models.Model):
"group_id": group_id, "group_id": group_id,
"date_planned": scheduled_date, "date_planned": scheduled_date,
"warehouse_id": warehouse, "warehouse_id": warehouse,
"partner_id": self.partner_id.id, "partner_id": self.partner_shipping_id.id,
"rma_id": self.id, "rma_id": self.id,
"priority": self.priority, "priority": self.priority,
} }

View File

@@ -96,13 +96,16 @@ class StockMove(models.Model):
partner = original_picking.partner_id partner = original_picking.partner_id
if hasattr(original_picking, "sale_id") and original_picking.sale_id: if hasattr(original_picking, "sale_id") and original_picking.sale_id:
partner_invoice_id = original_picking.sale_id.partner_invoice_id.id partner_invoice_id = original_picking.sale_id.partner_invoice_id.id
partner_shipping_id = original_picking.sale_id.partner_shipping_id.id
else: else:
partner_invoice_id = ( partner_invoice_id = partner.address_get(["invoice"]).get("invoice", False)
partner.address_get(["invoice"]).get("invoice", False), partner_shipping_id = partner.address_get(["delivery"]).get(
"delivery", False
) )
return { return {
"user_id": self.env.user.id, "user_id": self.env.user.id,
"partner_id": partner.id, "partner_id": partner.id,
"partner_shipping_id": partner_shipping_id,
"partner_invoice_id": partner_invoice_id, "partner_invoice_id": partner_invoice_id,
"origin": original_picking.name, "origin": original_picking.name,
"picking_id": original_picking.id, "picking_id": original_picking.id,

View File

@@ -44,6 +44,13 @@ class TestRma(SavepointCase):
"type": "invoice", "type": "invoice",
} }
) )
cls.partner_shipping = cls.res_partner.create(
{
"name": "Partner shipping test",
"parent_id": cls.partner.id,
"type": "delivery",
}
)
def _create_rma(self, partner=None, product=None, qty=None, location=None): def _create_rma(self, partner=None, product=None, qty=None, location=None):
rma_form = Form(self.env["rma"]) rma_form = Form(self.env["rma"])
@@ -181,7 +188,8 @@ class TestRma(SavepointCase):
rma.action_confirm() rma.action_confirm()
self.assertEqual( self.assertEqual(
e.exception.name, e.exception.name,
"Required field(s):\nCustomer\nInvoice Address\nProduct\nLocation", "Required field(s):\nCustomer\nShipping Address\nInvoice Address\n"
"Product\nLocation",
) )
with Form(rma) as rma_form: with Form(rma) as rma_form:
rma_form.partner_id = self.partner rma_form.partner_id = self.partner
@@ -531,7 +539,7 @@ class TestRma(SavepointCase):
# One picking per partner # One picking per partner
self.assertNotEqual(pick_1.partner_id, pick_2.partner_id) self.assertNotEqual(pick_1.partner_id, pick_2.partner_id)
self.assertEqual( self.assertEqual(
pick_1.partner_id, (rma_1 | rma_2 | rma_3).mapped("partner_id"), pick_1.partner_id, (rma_1 | rma_2 | rma_3).mapped("partner_shipping_id"),
) )
self.assertEqual(pick_2.partner_id, rma_4.partner_id) self.assertEqual(pick_2.partner_id, rma_4.partner_id)
# Each RMA of (rma_1, rma_2 and rma_3) is linked to a different # Each RMA of (rma_1, rma_2 and rma_3) is linked to a different

View File

@@ -239,6 +239,7 @@
context="{'search_default_customer':1, 'show_address': 1, 'show_vat': True}" context="{'search_default_customer':1, 'show_address': 1, 'show_vat': True}"
options="{'always_reload': True}" options="{'always_reload': True}"
/> />
<field name="partner_shipping_id" />
<field name="partner_invoice_id" /> <field name="partner_invoice_id" />
<field name="picking_id" options="{'no_create': True}" /> <field name="picking_id" options="{'no_create': True}" />
<field <field

View File

@@ -27,6 +27,7 @@ class CustomerPortal(CustomerPortal):
wizard_obj = request.env["sale.order.rma.wizard"] wizard_obj = request.env["sale.order.rma.wizard"]
# Set wizard line vals # Set wizard line vals
mapped_vals = {} mapped_vals = {}
partner_shipping_id = post.pop("partner_shipping_id", False)
for name, value in post.items(): for name, value in post.items():
row, field_name = name.split("-", 1) row, field_name = name.split("-", 1)
mapped_vals.setdefault(row, {}).update({field_name: value}) mapped_vals.setdefault(row, {}).update({field_name: value})
@@ -38,7 +39,11 @@ class CustomerPortal(CustomerPortal):
order = order_obj.browse(order_id).sudo() order = order_obj.browse(order_id).sudo()
location_id = order.warehouse_id.rma_loc_id.id location_id = order.warehouse_id.rma_loc_id.id
wizard = wizard_obj.with_context(active_id=order_id).create( wizard = wizard_obj.with_context(active_id=order_id).create(
{"line_ids": line_vals, "location_id": location_id} {
"line_ids": line_vals,
"location_id": location_id,
"partner_shipping_id": partner_shipping_id,
}
) )
rma = wizard.sudo().create_rma(from_portal=True) rma = wizard.sudo().create_rma(from_portal=True)
for rec in rma: for rec in rma:

View File

@@ -67,6 +67,48 @@
</ul> </ul>
</span> </span>
</div> </div>
<t
t-set="delivery_addresses"
t-value="sale_order.partner_id.commercial_partner_id.mapped('child_ids').filtered(lambda x: x.type in ['contact', 'delivery'])"
/>
<button
class="btn btn-primary btn-block mb8"
type="button"
data-toggle="collapse"
data-target="#delivery_address_picker"
aria-expanded="false"
><i
class="fa fa-truck"
/> Choose a delivery address</button>
<div
class="col-lg-12 collapse mt8"
id="delivery_address_picker"
>
<div data-toggle="buttons" class="row">
<label
t-attf-class="card mr4 btn btn-light"
t-foreach="delivery_addresses"
t-as="address"
>
<input
class="d-none"
type="radio"
name="partner_shipping_id"
t-att-value="address.id"
>
<strong>
<i
t-attf-class="text-secondary fa #{address.type == 'delivery' and 'fa-truck' or 'fa-user'}"
/>
<t t-esc="address.name" />
</strong>
<pre><h6
t-esc="address.contact_address"
/></pre>
</input>
</label>
</div>
</div>
<t <t
t-set="data_list" t-set="data_list"
t-value="sale_order.get_delivery_rma_data()" t-value="sale_order.get_delivery_rma_data()"

View File

@@ -27,6 +27,16 @@ class SaleOrderRmaWizard(models.TransientModel):
domain=_domain_location_id, domain=_domain_location_id,
default=lambda r: r.order_id.warehouse_id.rma_loc_id.id, default=lambda r: r.order_id.warehouse_id.rma_loc_id.id,
) )
commercial_partner_id = fields.Many2one(
comodel_name="res.partner",
related="order_id.partner_id.commercial_partner_id",
string="Commercial entity",
)
partner_shipping_id = fields.Many2one(
comodel_name="res.partner",
string="Shipping Address",
help="Will be used to return the goods when the RMA is completed",
)
def create_rma(self, from_portal=None): def create_rma(self, from_portal=None):
self.ensure_one() self.ensure_one()
@@ -147,9 +157,13 @@ class SaleOrderLineRmaWizard(models.TransientModel):
def _prepare_rma_values(self): def _prepare_rma_values(self):
self.ensure_one() self.ensure_one()
partner_shipping = (
self.wizard_id.partner_shipping_id or self.order_id.partner_shipping_id
)
return { return {
"partner_id": self.order_id.partner_id.id, "partner_id": self.order_id.partner_id.id,
"partner_invoice_id": self.order_id.partner_invoice_id.id, "partner_invoice_id": self.order_id.partner_invoice_id.id,
"partner_shipping_id": partner_shipping.id,
"origin": self.order_id.name, "origin": self.order_id.name,
"company_id": self.order_id.company_id.id, "company_id": self.order_id.company_id.id,
"location_id": self.wizard_id.location_id.id, "location_id": self.wizard_id.location_id.id,

View File

@@ -31,6 +31,11 @@
</field> </field>
</group> </group>
<group> <group>
<field name="commercial_partner_id" invisible="1" />
<field
name="partner_shipping_id"
domain="[('id', 'child_of', commercial_partner_id)]"
/>
<field <field
name="location_id" name="location_id"
options="{'no_create': True, 'no_open': True}" options="{'no_create': True, 'no_open': True}"