diff --git a/rma/__manifest__.py b/rma/__manifest__.py index a1de4a07..8f5c7313 100644 --- a/rma/__manifest__.py +++ b/rma/__manifest__.py @@ -2,12 +2,13 @@ { 'name': 'Hibou RMAs', - 'version': '13.0.1.2.0', + 'version': '13.0.1.3.0', 'category': 'Warehouse', 'author': 'Hibou Corp.', 'license': 'OPL-1', 'website': 'https://hibou.io/', 'depends': [ + 'hibou_professional', 'stock', 'delivery', ], diff --git a/rma/demo/rma_demo.xml b/rma/demo/rma_demo.xml index 17f405e0..94f78e70 100644 --- a/rma/demo/rma_demo.xml +++ b/rma/demo/rma_demo.xml @@ -42,4 +42,15 @@ make_to_stock + + + Return To Vendor + + + + + make_to_stock + + + \ No newline at end of file diff --git a/rma/models/rma.py b/rma/models/rma.py index d2e9847e..cdce249c 100644 --- a/rma/models/rma.py +++ b/rma/models/rma.py @@ -52,6 +52,7 @@ class RMATemplate(models.Model): company_id = fields.Many2one('res.company', 'Company') responsible_user_ids = fields.Many2many('res.users', string='Responsible Users', help='Users that get activities when creating RMA.') + next_rma_template_id = fields.Many2one('rma.template', string='Next RMA Template') def _portal_try_create(self, request_user, res_id, **kw): if self.usage == 'stock_picking': @@ -201,6 +202,7 @@ class RMA(models.Model): ('cancel', 'Cancelled'), ], string='State', default='draft', copy=False) company_id = fields.Many2one('res.company', 'Company') + parent_id = fields.Many2one('rma.rma') template_id = fields.Many2one('rma.template', string='Type', required=True) template_create_in_picking = fields.Boolean(related='template_id.create_in_picking') template_create_out_picking = fields.Boolean(related='template_id.create_out_picking') @@ -215,6 +217,7 @@ class RMA(models.Model): customer_description = fields.Html(string='Customer Instructions', related='template_id.customer_description') template_usage = fields.Selection(string='Template Usage', related='template_id.usage') validity_date = fields.Datetime(string='Expiration Date') + claim_number = fields.Char(string='Claim Number') invoice_ids = fields.Many2many('account.move', 'rma_invoice_rel', 'rma_id', @@ -363,6 +366,26 @@ class RMA(models.Model): 'in_picking_id': in_picking_id.id if in_picking_id else False, 'out_picking_id': out_picking_id.id if out_picking_id else False}) + def _next_rma_values(self): + return { + 'template_id': self.template_id.next_rma_template_id.id, + # Partners should be set when confirming or using the RTV wizard + # 'partner_id': self.partner_id.id, + # 'partner_shipping_id': self.partner_shipping_id.id, + 'parent_id': self.id, + 'lines': [(0, 0, { + 'product_id': l.product_id.id, + 'product_uom_id': l.product_uom_id.id, + 'product_uom_qty': l.product_uom_qty, + }) for l in self.lines] + } + + def _next_rma(self): + if self.template_id.next_rma_template_id: + # currently we do not want to automatically confirm them + # this is because we want to mass confirm and set picking to one partner/vendor + _ = self.create(self._next_rma_values()) + def action_done(self): for rma in self: if rma.in_picking_id and rma.in_picking_id.state not in ('done', 'cancel'): @@ -370,6 +393,67 @@ class RMA(models.Model): if rma.out_picking_id and rma.out_picking_id.state not in ('done', 'cancel'): raise UserError(_('Outbound picking not complete or cancelled.')) self.write({'state': 'done'}) + self._done_invoice() + self._next_rma() + + def _done_invoice(self): + for rma in self.filtered(lambda r: r.template_id.invoice_done): + # If you do NOT want to take part in the default invoicing functionality + # then your usage method (e.g. _invoice_values_sale_order) should be + # defined, and return nothing or extend _invoice_values to do the same + usage = rma.template_usage or '' + if hasattr(rma, '_invoice_values_' + usage): + values = getattr(rma, '_invoice_values_' + usage)() + else: + values = rma._invoice_values() + if values: + if hasattr(rma, '_invoice_' + usage): + getattr(rma, '_invoice_' + usage)(values) + else: + rma._invoice(values) + + def _invoice(self, invoice_values): + self.invoice_ids += self.env['account.move'].with_context(default_type=invoice_values['type']).create( + invoice_values) + + def _invoice_values(self): + self.ensure_one() + # special case for vendor return + supplier = self._context.get('rma_supplier') + if supplier is None and self.out_picking_id and self.out_picking_id.location_dest_id.usage == 'supplier': + supplier = True + + fiscal_position_id = self.env['account.fiscal.position'].get_fiscal_position( + self.partner_id.id, delivery_id=self.partner_shipping_id.id) + + invoice_values = { + 'type': 'in_refund' if supplier else 'out_refund', + 'partner_id': self.partner_id.id, + 'fiscal_position_id': fiscal_position_id, + } + + line_commands = [] + for rma_line in self.lines: + product = rma_line.product_id + accounts = product.product_tmpl_id.get_product_accounts() + account = accounts['expense'] if supplier else accounts['income'] + qty = rma_line.product_uom_qty + uom = rma_line.product_uom_id + price = product.standard_price if supplier else product.lst_price + if uom != product.uom_id: + price = product.uom_id._compute_price(price, uom) + line_commands.append((0, 0, { + 'product_id': product.id, + 'product_uom_id': uom.id, + 'name': product.name, + 'price_unit': price, + 'quantity': qty, + 'account_id': account.id, + 'tax_ids': [(6, 0, product.taxes_id.ids)], + })) + if line_commands: + invoice_values['invoice_line_ids'] = line_commands + return invoice_values def action_cancel(self): for rma in self: @@ -382,12 +466,18 @@ class RMA(models.Model): 'state': 'draft', 'in_picking_id': False, 'out_picking_id': False}) def _create_in_picking(self): + if self._context.get('rma_in_picking_id'): + # allow passing/setting by context to allow many RMA's to include the same pickings + return self.env['stock.picking'].browse(self._context.get('rma_in_picking_id')) if self.template_usage and hasattr(self, '_create_in_picking_' + self.template_usage): return getattr(self, '_create_in_picking_' + self.template_usage)() values = self.template_id._values_for_in_picking(self) return self.env['stock.picking'].sudo().create(values) def _create_out_picking(self): + if self._context.get('rma_out_picking_id'): + # allow passing/setting by context to allow many RMA's to include the same pickings + return self.env['stock.picking'].browse(self._context.get('rma_out_picking_id')) if self.template_usage and hasattr(self, '_create_out_picking_' + self.template_usage): return getattr(self, '_create_out_picking_' + self.template_usage)() values = self.template_id._values_for_out_picking(self) diff --git a/rma/tests/test_rma.py b/rma/tests/test_rma.py index 588cce36..ec70c3ac 100644 --- a/rma/tests/test_rma.py +++ b/rma/tests/test_rma.py @@ -14,8 +14,11 @@ class TestRMA(common.TransactionCase): self.product1 = self.env.ref('product.product_product_24') self.template_missing = self.env.ref('rma.template_missing_item') self.template_return = self.env.ref('rma.template_picking_return') + self.template_rtv = self.env.ref('rma.template_rtv') self.partner1 = self.env.ref('base.res_partner_2') self.user1 = self.env.ref('base.user_demo') + # Additional partner in tests or vendor in Return To Vendor + self.partner2 = self.env.ref('base.res_partner_12') def test_00_basic_rma(self): self.template_missing.responsible_user_ids += self.user1 @@ -244,3 +247,62 @@ class TestRMA(common.TransactionCase): # RMA cannot be completed because the inbound picking state is confirmed with self.assertRaises(UserError): rma2.action_done() + + def test_30_next_rma_rtv(self): + self.template_return.usage = False + self.template_return.in_require_return = False + self.template_return.next_rma_template_id = self.template_rtv + rma = self.env['rma.rma'].create({ + 'template_id': self.template_return.id, + 'partner_id': self.partner1.id, + 'partner_shipping_id': self.partner1.id, + }) + self.assertEqual(rma.state, 'draft') + rma_line = self.env['rma.line'].create({ + 'rma_id': rma.id, + 'product_id': self.product1.id, + 'product_uom_id': self.product1.uom_id.id, + 'product_uom_qty': 2.0, + }) + rma.action_confirm() + # Should have made pickings + self.assertEqual(rma.state, 'confirmed') + + # No outbound picking + self.assertFalse(rma.out_picking_id) + # Good inbound picking + self.assertTrue(rma.in_picking_id) + self.assertEqual(rma_line.product_id, rma.in_picking_id.move_lines.product_id) + self.assertEqual(rma_line.product_uom_qty, rma.in_picking_id.move_lines.product_uom_qty) + + with self.assertRaises(UserError): + rma.action_done() + + rma.in_picking_id.move_lines.quantity_done = 2.0 + rma.in_picking_id.action_done() + rma.action_done() + self.assertEqual(rma.state, 'done') + + # RTV RMA + rma_rtv = self.env['rma.rma'].search([('parent_id', '=', rma.id)]) + self.assertTrue(rma_rtv) + self.assertEqual(rma_rtv.state, 'draft') + + wiz = self.env['rma.make.rtv'].with_context(active_model='rma.rma', active_ids=rma_rtv.ids).create({}) + self.assertTrue(wiz.rma_line_ids) + wiz.partner_id = self.partner2 + wiz.create_batch() + self.assertTrue(rma_rtv.out_picking_id) + self.assertEqual(rma_rtv.out_picking_id.partner_id, self.partner2) + self.assertEqual(rma_rtv.state, 'confirmed') + + # ship and finish + rma_rtv.out_picking_id.move_lines.quantity_done = 2.0 + rma_rtv.out_picking_id.action_done() + rma_rtv.action_done() + self.assertEqual(rma_rtv.state, 'done') + + # ensure invoice and type + rtv_invoice = rma_rtv.invoice_ids + self.assertTrue(rtv_invoice) + self.assertEqual(rtv_invoice.type, 'in_refund') diff --git a/rma/views/portal_templates.xml b/rma/views/portal_templates.xml index abd1728e..0692be42 100644 --- a/rma/views/portal_templates.xml +++ b/rma/views/portal_templates.xml @@ -138,8 +138,8 @@
- Product image + Product Image + Product Image
@@ -237,8 +237,8 @@
- Product image + Product Image + Product Image
diff --git a/rma/views/rma_views.xml b/rma/views/rma_views.xml index f636ba54..42ee959a 100644 --- a/rma/views/rma_views.xml +++ b/rma/views/rma_views.xml @@ -26,6 +26,7 @@
+ @@ -35,6 +36,7 @@
@@ -172,6 +174,7 @@ + diff --git a/rma/wizard/__init__.py b/rma/wizard/__init__.py index 1cbabc08..38eeb3bd 100644 --- a/rma/wizard/__init__.py +++ b/rma/wizard/__init__.py @@ -1,3 +1,4 @@ # Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. from . import rma_lines +from . import rma_make_rtv diff --git a/rma/wizard/rma_make_rtv.py b/rma/wizard/rma_make_rtv.py new file mode 100644 index 00000000..728e3905 --- /dev/null +++ b/rma/wizard/rma_make_rtv.py @@ -0,0 +1,90 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + +from odoo import api, fields, models +from odoo.exceptions import UserError + + +class RMAMakeRTV(models.TransientModel): + _name = 'rma.make.rtv' + _description = 'Make RTV Batch' + + partner_id = fields.Many2one('res.partner', string='Vendor') + partner_shipping_id = fields.Many2one('res.partner', string='Shipping Address') + rma_line_ids = fields.One2many('rma.make.rtv.line', 'rma_make_rtv_id', string='Lines') + + @api.model + def default_get(self, fields): + result = super().default_get(fields) + if 'rma_line_ids' in fields and self._context.get('active_model') == 'rma.rma' and self._context.get('active_ids'): + active_ids = self._context.get('active_ids') + rmas = self.env['rma.rma'].browse(active_ids) + result['rma_line_ids'] = [(0, 0, { + 'rma_id': r.id, + 'rma_state': r.state, + 'rma_claim_number': r.claim_number, + }) for r in rmas] + rma_partner = rmas.mapped('partner_id') + if rma_partner: + result['partner_id'] = rma_partner[0].id + return result + + def create_batch(self): + self.ensure_one() + if self.rma_line_ids.filtered(lambda rl: rl.rma_id.state != 'draft'): + raise UserError('All RMAs must be in the draft state.') + rma_partner = self.rma_line_ids.mapped('rma_id.partner_id') + if rma_partner and len(rma_partner) != 1: + raise UserError('All RMAs must be for the same partner.') + elif not rma_partner and not self.partner_id: + raise UserError('Please select a Vendor') + elif not rma_partner: + rma_partner = self.partner_id + rma_partner_shipping = self.partner_shipping_id or rma_partner + # update all RMA's to the currently selected vendor + self.rma_line_ids.mapped('rma_id').write({ + 'partner_id': rma_partner.id, + 'partner_shipping_id': rma_partner_shipping.id, + }) + if len(self.rma_line_ids.mapped('rma_id.template_id')) != 1: + raise UserError('All RMAs must be of the same template.') + + in_values = None + out_values = None + for rma in self.rma_line_ids.mapped('rma_id'): + if rma.template_id.create_in_picking: + if not in_values: + in_values = rma.template_id._values_for_in_picking(rma) + in_values['origin'] = [in_values['origin']] + else: + other_in_values = rma.template_id._values_for_in_picking(rma) + in_values['move_lines'] += other_in_values['move_lines'] + if rma.template_id.create_out_picking: + if not out_values: + out_values = rma.template_id._values_for_out_picking(rma) + out_values['origin'] = [out_values['origin']] + else: + other_out_values = rma.template_id._values_for_out_picking(rma) + out_values['move_lines'] += other_out_values['move_lines'] + in_picking_id = False + out_picking_id = False + if in_values: + in_values['origin'] = ', '.join(in_values['origin']) + in_picking = self.env['stock.picking'].sudo().create(in_values) + in_picking_id = in_picking.id + if out_values: + out_values['origin'] = ', '.join(out_values['origin']) + out_picking = self.env['stock.picking'].sudo().create(out_values) + out_picking_id = out_picking.id + rmas = self.rma_line_ids.mapped('rma_id').with_context(rma_in_picking_id=in_picking_id, rma_out_picking_id=out_picking_id) + # action_confirm known to be multi-aware and makes only one context + rmas.action_confirm() + + +class RMAMakeRTVLine(models.TransientModel): + _name = 'rma.make.rtv.line' + _description = 'Make RTV Batch RMA' + + rma_make_rtv_id = fields.Many2one('rma.make.rtv') + rma_id = fields.Many2one('rma.rma') + rma_state = fields.Selection(related='rma_id.state') + rma_claim_number = fields.Char(related='rma_id.claim_number', readonly=False) diff --git a/rma/wizard/rma_make_rtv_views.xml b/rma/wizard/rma_make_rtv_views.xml new file mode 100644 index 00000000..b7da2d82 --- /dev/null +++ b/rma/wizard/rma_make_rtv_views.xml @@ -0,0 +1,41 @@ + + + + + Return To Vendor + rma.make.rtv + +
+

+ RMAs will be batched to pick simultaneously. +

+ + + + + + + + + + + +
+
+
+
+
+ + + RMA Make RTV + ir.actions.act_window + rma.make.rtv + form + new + + list + + +
diff --git a/rma_product_cores/__manifest__.py b/rma_product_cores/__manifest__.py index 1d02720e..979a0c7a 100755 --- a/rma_product_cores/__manifest__.py +++ b/rma_product_cores/__manifest__.py @@ -3,7 +3,7 @@ { 'name': 'RMA - Product Cores', 'author': 'Hibou Corp. ', - 'version': '13.0.1.0.0', + 'version': '13.0.1.0.1', 'license': 'OPL-1', 'category': 'Tools', 'summary': 'RMA Product Cores', @@ -12,6 +12,7 @@ RMA Product Cores - Return core products from customers. """, 'website': 'https://hibou.io/', 'depends': [ + 'hibou_professional', 'product_cores', 'rma_sale', ], diff --git a/rma_product_cores/models/rma.py b/rma_product_cores/models/rma.py index 7cda5acf..1d52747c 100644 --- a/rma_product_cores/models/rma.py +++ b/rma_product_cores/models/rma.py @@ -120,6 +120,10 @@ class RMA(models.Model): return res2 return res + def _invoice_values_product_core_sale(self): + # the RMA invoice API will not be used as invoicing will happen at the SO level + return False + def _get_dirty_core_from_service_line(self, line): original_product_line = line.core_line_id return original_product_line.product_id.product_core_id diff --git a/rma_product_cores/views/portal_templates.xml b/rma_product_cores/views/portal_templates.xml index 84c02b56..ff1818d8 100644 --- a/rma_product_cores/views/portal_templates.xml +++ b/rma_product_cores/views/portal_templates.xml @@ -42,8 +42,8 @@
- Product image + Product Image + Product Image
diff --git a/rma_sale/__manifest__.py b/rma_sale/__manifest__.py index 2cbf748b..2f3f70ff 100644 --- a/rma_sale/__manifest__.py +++ b/rma_sale/__manifest__.py @@ -2,12 +2,13 @@ { 'name': 'Hibou RMAs for Sale Orders', - 'version': '13.0.1.1.0', + 'version': '13.0.1.2.0', 'category': 'Sale', 'author': 'Hibou Corp.', 'license': 'OPL-1', 'website': 'https://hibou.io/', 'depends': [ + 'hibou_professional', 'rma', 'sale', 'sales_team', diff --git a/rma_sale/models/product.py b/rma_sale/models/product.py index 2e338a39..06308b76 100644 --- a/rma_sale/models/product.py +++ b/rma_sale/models/product.py @@ -14,3 +14,13 @@ class ProductTemplate(models.Model): 'A positive number will allow the product to be ' 'returned up to that number of days. A negative ' 'number prevents the return of the product.') + + rma_sale_warranty_validity = fields.Integer(string='RMA Eligible Days (Sale Warranty)', + help='Determines the number of days from the time ' + 'of the sale that the product is eligible to ' + 'be returned for warranty claims. ' + '0 (default) will allow the product to be ' + 'returned for an indefinite period of time. ' + 'A positive number will allow the product to be ' + 'returned up to that number of days. A negative ' + 'number prevents the return of the product.') diff --git a/rma_sale/models/rma.py b/rma_sale/models/rma.py index 03344398..fb800915 100644 --- a/rma_sale/models/rma.py +++ b/rma_sale/models/rma.py @@ -20,6 +20,9 @@ class RMATemplate(models.Model): _inherit = 'rma.template' usage = fields.Selection(selection_add=[('sale_order', 'Sale Order')]) + sale_order_warranty = fields.Boolean(string='Sale Order Warranty', + help='Determines if the regular return validity or ' + 'Warranty validity is used.') so_decrement_order_qty = fields.Boolean(string='SO Decrement Ordered Qty.', help='When completing the RMA, the Ordered Quantity will be decremented by ' 'the RMA qty.') @@ -87,7 +90,10 @@ class RMATemplate(models.Model): return super(RMATemplate, self)._portal_values(request_user, res_id=res_id) def _rma_sale_line_validity(self, so_line): - validity_days = so_line.product_id.rma_sale_validity + if self.sale_order_warranty: + validity_days = so_line.product_id.rma_sale_warranty_validity + else: + validity_days = so_line.product_id.rma_sale_validity if validity_days < 0: return '' elif validity_days > 0: @@ -195,6 +201,10 @@ class RMA(models.Model): pass return sale_orders.mapped('invoice_ids') - original_invoices + def _invoice_values_sale_order(self): + # the RMA invoice API will not be used as invoicing will happen at the SO level + return False + def action_add_so_lines(self): make_line_obj = self.env['rma.sale.make.lines'] for rma in self: diff --git a/rma_sale/tests/test_rma.py b/rma_sale/tests/test_rma.py index 446260d6..7cd4d329 100644 --- a/rma_sale/tests/test_rma.py +++ b/rma_sale/tests/test_rma.py @@ -167,7 +167,140 @@ class TestRMASale(TestRMA): # Existing lot cannot be re-used. with self.assertRaises(ValidationError): rma2.in_picking_id.action_done() - + # RMA cannot be completed because the inbound picking state is confirmed with self.assertRaises(UserError): rma2.action_done() + + def test_30_product_sale_return_warranty(self): + self.template_sale_return.write({ + 'usage': 'sale_order', + 'invoice_done': True, + 'sale_order_warranty': True, + 'in_to_refund': True, + 'so_decrement_order_qty': False, # invoice on decremented delivered not decremented order + 'next_rma_template_id': self.template_rtv.id, + }) + + validity = 100 # eligible for 100 days + warranty_validity = validity + 100 # eligible for 200 days + + self.product1.write({ + 'rma_sale_validity': validity, + 'rma_sale_warranty_validity': warranty_validity, + 'type': 'product', + 'invoice_policy': 'delivery', + 'tracking': 'serial', + 'standard_price': 1.5, + }) + + order = self.env['sale.order'].create({ + 'partner_id': self.partner1.id, + 'partner_invoice_id': self.partner1.id, + 'partner_shipping_id': self.partner1.id, + 'user_id': self.user1.id, + 'order_line': [(0, 0, { + 'product_id': self.product1.id, + 'product_uom_qty': 1.0, + 'product_uom': self.product1.uom_id.id, + 'price_unit': 10.0, + })] + }) + order.action_confirm() + self.assertTrue(order.state in ('sale', 'done')) + self.assertEqual(len(order.picking_ids), 1, 'Tests only run with single stage delivery.') + + # Try to RMA item not delivered yet + rma = self.env['rma.rma'].create({ + 'template_id': self.template_sale_return.id, + 'partner_id': self.partner1.id, + 'partner_shipping_id': self.partner1.id, + 'sale_order_id': order.id, + }) + self.assertEqual(rma.state, 'draft') + # Do not allow warranty return. + self.product1.rma_sale_warranty_validity = -1 + wizard = self.env['rma.sale.make.lines'].with_user(self.user1).create({'rma_id': rma.id}) + self.assertEqual(wizard.line_ids.qty_delivered, 0.0) + wizard.line_ids.product_uom_qty = 1.0 + + with self.assertRaises(UserError): + wizard.add_lines() + + # Allows returns, but not forever + self.product1.rma_sale_warranty_validity = warranty_validity + original_date_order = order.date_order + order.write({'date_order': original_date_order - timedelta(days=warranty_validity+1)}) + wizard = self.env['rma.sale.make.lines'].with_user(self.user1).create({'rma_id': rma.id}) + self.assertEqual(wizard.line_ids.qty_delivered, 0.0) + wizard.line_ids.product_uom_qty = 1.0 + with self.assertRaises(UserError): + wizard.add_lines() + + # Allows returns due to date, due to warranty option + order.write({'date_order': original_date_order - timedelta(days=validity+1)}) + wizard = self.env['rma.sale.make.lines'].with_user(self.user1).create({'rma_id': rma.id}) + self.assertEqual(wizard.line_ids.qty_delivered, 0.0) + wizard.line_ids.product_uom_qty = 1.0 + wizard.add_lines() + + # finish outbound so that we can invoice. + order.picking_ids.action_assign() + pack_opt = order.picking_ids.move_line_ids[0] + lot = self.env['stock.production.lot'].create({ + 'product_id': self.product1.id, + 'name': 'X100', + 'product_uom_id': self.product1.uom_id.id, + 'company_id': self.env.user.company_id.id, + }) + pack_opt.qty_done = 1.0 + pack_opt.lot_id = lot + order.picking_ids.button_validate() + self.assertEqual(order.picking_ids.state, 'done') + self.assertEqual(order.order_line.qty_delivered, 1.0) + + # Invoice the order so that only the core product is invoiced at the end... + self.assertFalse(order.invoice_ids) + wiz = self.env['sale.advance.payment.inv'].with_context(active_ids=order.ids).create({}) + wiz.create_invoices() + order.flush() + self.assertTrue(order.invoice_ids) + order_invoice = order.invoice_ids + + self.assertEqual(rma.lines.product_id, self.product1) + rma.action_confirm() + self.assertTrue(rma.in_picking_id) + self.assertEqual(rma.in_picking_id.state, 'assigned') + pack_opt = rma.in_picking_id.move_line_ids[0] + pack_opt.lot_id = lot.id + pack_opt.qty_done = 1.0 + rma.in_picking_id.button_validate() + self.assertEqual(rma.in_picking_id.state, 'done') + order.flush() + # self.assertEqual(order.order_line.qty_delivered, 0.0) + rma.action_done() + self.assertEqual(rma.state, 'done') + order.flush() + + rma_invoice = rma.invoice_ids + self.assertTrue(rma_invoice) + sale_line = rma_invoice.invoice_line_ids.filtered(lambda l: l.sale_line_ids) + so_line = sale_line.sale_line_ids + self.assertTrue(sale_line) + self.assertEqual(sale_line.price_unit, so_line.price_unit) + + # Invoices do not have their anglo-saxon cost lines until they post + order_invoice.post() + rma_invoice.post() + + # Find the return to vendor RMA + rtv_rma = self.env['rma.rma'].search([('parent_id', '=', rma.id)]) + self.assertTrue(rtv_rma) + self.assertFalse(rtv_rma.out_picking_id) + + wiz = self.env['rma.make.rtv'].with_context(active_model='rma.rma', active_ids=rtv_rma.ids).create({}) + self.assertTrue(wiz.rma_line_ids) + wiz.partner_id = self.partner2 + wiz.create_batch() + self.assertTrue(rtv_rma.out_picking_id) + self.assertEqual(rtv_rma.out_picking_id.partner_id, self.partner2) diff --git a/rma_sale/views/portal_templates.xml b/rma_sale/views/portal_templates.xml index 6340352d..bb97b9b7 100644 --- a/rma_sale/views/portal_templates.xml +++ b/rma_sale/views/portal_templates.xml @@ -57,8 +57,8 @@
- Product image + Product Image + Product Image
diff --git a/rma_sale/views/product_views.xml b/rma_sale/views/product_views.xml index 4fa17000..41ea3e62 100644 --- a/rma_sale/views/product_views.xml +++ b/rma_sale/views/product_views.xml @@ -9,6 +9,7 @@ + @@ -22,6 +23,7 @@ + diff --git a/rma_sale/views/rma_views.xml b/rma_sale/views/rma_views.xml index f97ce8a8..9c39bf9b 100644 --- a/rma_sale/views/rma_views.xml +++ b/rma_sale/views/rma_views.xml @@ -9,6 +9,7 @@ +