[IMP] : black, isort

This commit is contained in:
ahenriquez
2020-01-22 11:18:04 +01:00
parent c86a3bf221
commit 9328d47d73
9 changed files with 698 additions and 583 deletions

View File

@@ -2,24 +2,24 @@
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
{ {
'name': 'RMA Account', "name": "RMA Account",
'version': '12.0.1.0.0', "version": "12.0.1.0.0",
'license': 'LGPL-3', "license": "LGPL-3",
'category': 'RMA', "category": "RMA",
'summary': 'Integrates RMA with Invoice Processing', "summary": "Integrates RMA with Invoice Processing",
'author': "Eficent, Odoo Community Association (OCA)", "author": "Eficent, Odoo Community Association (OCA)",
'website': 'https://github.com/Eficent/stock-rma', "website": "https://github.com/Eficent/stock-rma",
'depends': ['stock_account', 'rma'], "depends": ["stock_account", "rma"],
'demo': ['data/rma_operation.xml'], "demo": ["data/rma_operation.xml"],
'data': [ "data": [
'security/ir.model.access.csv', "security/ir.model.access.csv",
'views/rma_order_view.xml', "views/rma_order_view.xml",
'views/rma_operation_view.xml', "views/rma_operation_view.xml",
'views/rma_order_line_view.xml', "views/rma_order_line_view.xml",
'views/invoice_view.xml', "views/invoice_view.xml",
'views/rma_account_menu.xml', "views/rma_account_menu.xml",
'wizards/rma_add_invoice.xml', "wizards/rma_add_invoice.xml",
'wizards/rma_refund.xml', "wizards/rma_refund.xml",
], ],
'installable': True, "installable": True,
} }

View File

@@ -8,92 +8,89 @@ from odoo.tools.float_utils import float_compare
class AccountInvoice(models.Model): class AccountInvoice(models.Model):
_inherit = "account.invoice" _inherit = "account.invoice"
@api.depends('invoice_line_ids.rma_line_ids') @api.depends("invoice_line_ids.rma_line_ids")
def _compute_rma_count(self): def _compute_rma_count(self):
for inv in self: for inv in self:
rmas = self.mapped('invoice_line_ids.rma_line_ids') rmas = self.mapped("invoice_line_ids.rma_line_ids")
inv.rma_count = len(rmas) inv.rma_count = len(rmas)
def _prepare_invoice_line_from_rma_line(self, line): def _prepare_invoice_line_from_rma_line(self, line):
qty = line.qty_to_refund qty = line.qty_to_refund
if float_compare( if float_compare(qty, 0.0, precision_rounding=line.uom_id.rounding) <= 0:
qty, 0.0, precision_rounding=line.uom_id.rounding) <= 0:
qty = 0.0 qty = 0.0
# Todo fill taxes from somewhere # Todo fill taxes from somewhere
invoice_line = self.env['account.invoice.line'] invoice_line = self.env["account.invoice.line"]
data = { data = {
'purchase_line_id': line.id, "purchase_line_id": line.id,
'name': line.name + ': '+line.name, "name": line.name + ": " + line.name,
'origin': line.origin, "origin": line.origin,
'uom_id': line.uom_id.id, "uom_id": line.uom_id.id,
'product_id': line.product_id.id, "product_id": line.product_id.id,
'account_id': invoice_line.with_context( "account_id": invoice_line.with_context(
{'journal_id': self.journal_id.id, {"journal_id": self.journal_id.id, "type": "in_invoice"}
'type': 'in_invoice'})._default_account(), )._default_account(),
'price_unit': line.company_id.currency_id.with_context( "price_unit": line.company_id.currency_id.with_context(
date=self.date_invoice).compute( date=self.date_invoice
line.price_unit, self.currency_id, round=False), ).compute(line.price_unit, self.currency_id, round=False),
'quantity': qty, "quantity": qty,
'discount': 0.0, "discount": 0.0,
'rma_line_ids': [(4, line.id)], "rma_line_ids": [(4, line.id)],
} }
return data return data
@api.onchange('add_rma_line_id') @api.onchange("add_rma_line_id")
def on_change_add_rma_line_id(self): def on_change_add_rma_line_id(self):
if not self.add_rma_line_id: if not self.add_rma_line_id:
return {} return {}
if not self.partner_id: if not self.partner_id:
self.partner_id = self.add_rma_line_id.partner_id.id self.partner_id = self.add_rma_line_id.partner_id.id
new_line = self.env['account.invoice.line'] new_line = self.env["account.invoice.line"]
if self.add_rma_line_id not in ( if self.add_rma_line_id not in (self.invoice_line_ids.mapped("rma_line_id")):
self.invoice_line_ids.mapped('rma_line_id')): data = self._prepare_invoice_line_from_rma_line(self.add_rma_line_id)
data = self._prepare_invoice_line_from_rma_line(
self.add_rma_line_id)
new_line = new_line.new(data) new_line = new_line.new(data)
new_line._set_additional_fields(self) new_line._set_additional_fields(self)
self.invoice_line_ids += new_line self.invoice_line_ids += new_line
self.add_rma_line_id = False self.add_rma_line_id = False
return {} return {}
rma_count = fields.Integer( rma_count = fields.Integer(compute="_compute_rma_count", string="# of RMA")
compute='_compute_rma_count', string='# of RMA')
add_rma_line_id = fields.Many2one( add_rma_line_id = fields.Many2one(
comodel_name='rma.order.line', comodel_name="rma.order.line",
string="Add from RMA line", string="Add from RMA line",
ondelete="set null", ondelete="set null",
help="Create a refund in based on an existing rma_line") help="Create a refund in based on an existing rma_line",
)
@api.multi @api.multi
def action_view_rma_supplier(self): def action_view_rma_supplier(self):
action = self.env.ref('rma.action_rma_supplier_lines') action = self.env.ref("rma.action_rma_supplier_lines")
result = action.read()[0] result = action.read()[0]
rma_ids = self.mapped('invoice_line_ids.rma_line_ids').ids rma_ids = self.mapped("invoice_line_ids.rma_line_ids").ids
if rma_ids: if rma_ids:
# choose the view_mode accordingly # choose the view_mode accordingly
if len(rma_ids) > 1: if len(rma_ids) > 1:
result['domain'] = [('id', 'in', rma_ids)] result["domain"] = [("id", "in", rma_ids)]
else: else:
res = self.env.ref('rma.view_rma_line_supplier_form', False) res = self.env.ref("rma.view_rma_line_supplier_form", False)
result['views'] = [(res and res.id or False, 'form')] result["views"] = [(res and res.id or False, "form")]
result['res_id'] = rma_ids[0] result["res_id"] = rma_ids[0]
return result return result
@api.multi @api.multi
def action_view_rma_customer(self): def action_view_rma_customer(self):
action = self.env.ref('rma.action_rma_customer_lines') action = self.env.ref("rma.action_rma_customer_lines")
result = action.read()[0] result = action.read()[0]
rma_ids = self.mapped('invoice_line_ids.rma_line_ids').ids rma_ids = self.mapped("invoice_line_ids.rma_line_ids").ids
if rma_ids: if rma_ids:
# choose the view_mode accordingly # choose the view_mode accordingly
if len(rma_ids) > 1: if len(rma_ids) > 1:
result['domain'] = [('id', 'in', rma_ids)] result["domain"] = [("id", "in", rma_ids)]
else: else:
res = self.env.ref('rma.view_rma_line_form', False) res = self.env.ref("rma.view_rma_line_form", False)
result['views'] = [(res and res.id or False, 'form')] result["views"] = [(res and res.id or False, "form")]
result['res_id'] = rma_ids[0] result["res_id"] = rma_ids[0]
return result return result
@@ -101,15 +98,13 @@ class AccountInvoiceLine(models.Model):
_inherit = "account.invoice.line" _inherit = "account.invoice.line"
@api.model @api.model
def name_search(self, name, args=None, operator='ilike', limit=100): def name_search(self, name, args=None, operator="ilike", limit=100):
"""Allows to search by Invoice number. This has to be done this way, """Allows to search by Invoice number. This has to be done this way,
as Odoo adds extra args to name_search on _name_search method that as Odoo adds extra args to name_search on _name_search method that
will make impossible to get the desired result.""" will make impossible to get the desired result."""
if not args: if not args:
args = [] args = []
lines = self.search( lines = self.search([("invoice_id.number", operator, name)] + args, limit=limit)
[('invoice_id.number', operator, name)] + args, limit=limit,
)
res = lines.name_get() res = lines.name_get()
if limit: if limit:
limit_rest = limit - len(lines) limit_rest = limit - len(lines)
@@ -117,32 +112,44 @@ class AccountInvoiceLine(models.Model):
# limit can be 0 or None representing infinite # limit can be 0 or None representing infinite
limit_rest = limit limit_rest = limit
if limit_rest or not limit: if limit_rest or not limit:
args += [('id', 'not in', lines.ids)] args += [("id", "not in", lines.ids)]
res += super(AccountInvoiceLine, self).name_search( res += super(AccountInvoiceLine, self).name_search(
name, args=args, operator=operator, limit=limit_rest, name, args=args, operator=operator, limit=limit_rest
) )
return res return res
@api.multi @api.multi
def name_get(self): def name_get(self):
res = [] res = []
if self.env.context.get('rma'): if self.env.context.get("rma"):
for inv in self: for inv in self:
if inv.invoice_id.reference: if inv.invoice_id.reference:
res.append( res.append(
(inv.id, (
"INV:%s | REF:%s | ORIG:%s | PART:%s | QTY:%s" % ( inv.id,
inv.invoice_id.number or '', "INV:%s | REF:%s | ORIG:%s | PART:%s | QTY:%s"
inv.origin or '', % (
inv.invoice_id.reference or "", inv.invoice_id.number or "",
inv.product_id.name, inv.quantity))) inv.origin or "",
inv.invoice_id.reference or "",
inv.product_id.name,
inv.quantity,
),
)
)
elif inv.invoice_id.number: elif inv.invoice_id.number:
res.append( res.append(
(inv.id, (
"INV:%s | ORIG:%s | PART:%s | QTY:%s" % ( inv.id,
inv.invoice_id.number or '', "INV:%s | ORIG:%s | PART:%s | QTY:%s"
inv.origin or '', % (
inv.product_id.name, inv.quantity))) inv.invoice_id.number or "",
inv.origin or "",
inv.product_id.name,
inv.quantity,
),
)
)
else: else:
res.append(super(AccountInvoiceLine, inv).name_get()[0]) res.append(super(AccountInvoiceLine, inv).name_get()[0])
return res return res
@@ -152,18 +159,21 @@ class AccountInvoiceLine(models.Model):
@api.multi @api.multi
def _compute_rma_count(self): def _compute_rma_count(self):
for invl in self: for invl in self:
rma_lines = invl.mapped('rma_line_ids') rma_lines = invl.mapped("rma_line_ids")
invl.rma_line_count = len(rma_lines) invl.rma_line_count = len(rma_lines)
rma_line_count = fields.Integer( rma_line_count = fields.Integer(compute="_compute_rma_count", string="# of RMA")
compute='_compute_rma_count', string='# of RMA')
rma_line_ids = fields.One2many( rma_line_ids = fields.One2many(
comodel_name='rma.order.line', inverse_name='invoice_line_id', comodel_name="rma.order.line",
string="RMA", readonly=True, inverse_name="invoice_line_id",
help="This will contain the RMA lines for the invoice line") string="RMA",
readonly=True,
help="This will contain the RMA lines for the invoice line",
)
rma_line_id = fields.Many2one( rma_line_id = fields.Many2one(
comodel_name='rma.order.line', comodel_name="rma.order.line",
string="RMA line refund", string="RMA line refund",
ondelete="set null", ondelete="set null",
help="This will contain the rma line that originated the refund line") help="This will contain the rma line that originated the refund line",
)

View File

@@ -5,10 +5,15 @@ from odoo import fields, models
class RmaOperation(models.Model): class RmaOperation(models.Model):
_inherit = 'rma.operation' _inherit = "rma.operation"
refund_policy = fields.Selection([ refund_policy = fields.Selection(
('no', 'No refund'), ('ordered', 'Based on Ordered Quantities'), [
('delivered', 'Based on Delivered Quantities'), ("no", "No refund"),
('received', 'Based on Received Quantities')], string="Refund Policy", ("ordered", "Based on Ordered Quantities"),
default='no') ("delivered", "Based on Delivered Quantities"),
("received", "Based on Received Quantities"),
],
string="Refund Policy",
default="no",
)

View File

@@ -10,58 +10,64 @@ class RmaOrder(models.Model):
@api.multi @api.multi
def _compute_invoice_refund_count(self): def _compute_invoice_refund_count(self):
for rec in self: for rec in self:
invoices = rec.mapped( invoices = rec.mapped("rma_line_ids.refund_line_ids.invoice_id")
'rma_line_ids.refund_line_ids.invoice_id')
rec.invoice_refund_count = len(invoices) rec.invoice_refund_count = len(invoices)
@api.multi @api.multi
def _compute_invoice_count(self): def _compute_invoice_count(self):
for rec in self: for rec in self:
invoices = rec.mapped('rma_line_ids.invoice_id') invoices = rec.mapped("rma_line_ids.invoice_id")
rec.invoice_count = len(invoices) rec.invoice_count = len(invoices)
add_invoice_id = fields.Many2one( add_invoice_id = fields.Many2one(
comodel_name='account.invoice', string='Add Invoice', comodel_name="account.invoice",
ondelete='set null', readonly=True, string="Add Invoice",
ondelete="set null",
readonly=True,
) )
invoice_refund_count = fields.Integer( invoice_refund_count = fields.Integer(
compute='_compute_invoice_refund_count', string='# of Refunds') compute="_compute_invoice_refund_count", string="# of Refunds"
)
invoice_count = fields.Integer( invoice_count = fields.Integer(
compute='_compute_invoice_count', string='# of Invoices') compute="_compute_invoice_count", string="# of Invoices"
)
def _prepare_rma_line_from_inv_line(self, line): def _prepare_rma_line_from_inv_line(self, line):
if self.type == 'customer': if self.type == "customer":
operation =\ operation = (
self.rma_line_ids.product_id.rma_customer_operation_id or \ self.rma_line_ids.product_id.rma_customer_operation_id
self.rma_line_ids.product_id.categ_id.rma_customer_operation_id or self.rma_line_ids.product_id.categ_id.rma_customer_operation_id
)
else: else:
operation =\ operation = (
self.rma_line_ids.product_id.rma_supplier_operation_id or \ self.rma_line_ids.product_id.rma_supplier_operation_id
self.rma_line_ids.product_id.categ_id.rma_supplier_operation_id or self.rma_line_ids.product_id.categ_id.rma_supplier_operation_id
)
data = { data = {
'invoice_line_id': line.id, "invoice_line_id": line.id,
'product_id': line.product_id.id, "product_id": line.product_id.id,
'name': line.name, "name": line.name,
'origin': line.invoice_id.number, "origin": line.invoice_id.number,
'uom_id': line.uom_id.id, "uom_id": line.uom_id.id,
'operation_id': operation, "operation_id": operation,
'product_qty': line.quantity, "product_qty": line.quantity,
'price_unit': line.invoice_id.currency_id.compute( "price_unit": line.invoice_id.currency_id.compute(
line.price_unit, line.currency_id, round=False), line.price_unit, line.currency_id, round=False
'rma_id': self.id ),
"rma_id": self.id,
} }
return data return data
@api.onchange('add_invoice_id') @api.onchange("add_invoice_id")
def on_change_invoice(self): def on_change_invoice(self):
if not self.add_invoice_id: if not self.add_invoice_id:
return {} return {}
if not self.partner_id: if not self.partner_id:
self.partner_id = self.add_invoice_id.partner_id.id self.partner_id = self.add_invoice_id.partner_id.id
new_lines = self.env['rma.order.line'] new_lines = self.env["rma.order.line"]
for line in self.add_invoice_id.invoice_line_ids: for line in self.add_invoice_id.invoice_line_ids:
# Load a PO line only once # Load a PO line only once
if line in self.rma_line_ids.mapped('invoice_line_id'): if line in self.rma_line_ids.mapped("invoice_line_id"):
continue continue
data = self._prepare_rma_line_from_inv_line(line) data = self._prepare_rma_line_from_inv_line(line)
new_line = new_lines.new(data) new_line = new_lines.new(data)
@@ -76,48 +82,46 @@ class RmaOrder(models.Model):
@api.model @api.model
def prepare_rma_line(self, origin_rma, rma_id, line): def prepare_rma_line(self, origin_rma, rma_id, line):
line_values = super(RmaOrder, self).prepare_rma_line( line_values = super(RmaOrder, self).prepare_rma_line(origin_rma, rma_id, line)
origin_rma, rma_id, line) line_values["invoice_address_id"] = line.invoice_address_id.id
line_values['invoice_address_id'] = line.invoice_address_id.id
return line_values return line_values
@api.model @api.model
def _prepare_rma_data(self, partner, origin_rma): def _prepare_rma_data(self, partner, origin_rma):
res = super(RmaOrder, self)._prepare_rma_data(partner, origin_rma) res = super(RmaOrder, self)._prepare_rma_data(partner, origin_rma)
res['invoice_address_id'] = partner.id res["invoice_address_id"] = partner.id
return res return res
@api.multi @api.multi
def action_view_invoice_refund(self): def action_view_invoice_refund(self):
action = self.env.ref('account.action_invoice_tree2') action = self.env.ref("account.action_invoice_tree2")
result = action.read()[0] result = action.read()[0]
invoice_ids = self.mapped( invoice_ids = self.mapped("rma_line_ids.refund_line_ids.invoice_id").ids
'rma_line_ids.refund_line_ids.invoice_id').ids
if invoice_ids: if invoice_ids:
# choose the view_mode accordingly # choose the view_mode accordingly
if len(invoice_ids) > 1: if len(invoice_ids) > 1:
result['domain'] = [('id', 'in', invoice_ids)] result["domain"] = [("id", "in", invoice_ids)]
else: else:
res = self.env.ref('account.invoice_supplier_form', False) res = self.env.ref("account.invoice_supplier_form", False)
result['views'] = [(res and res.id or False, 'form')] result["views"] = [(res and res.id or False, "form")]
result['res_id'] = invoice_ids[0] result["res_id"] = invoice_ids[0]
return result return result
@api.multi @api.multi
def action_view_invoice(self): def action_view_invoice(self):
if self.type == "supplier": if self.type == "supplier":
action = self.env.ref('account.action_invoice_tree2') action = self.env.ref("account.action_invoice_tree2")
res = self.env.ref('account.invoice_supplier_form', False) res = self.env.ref("account.invoice_supplier_form", False)
else: else:
action = self.env.ref('account.action_invoice_tree') action = self.env.ref("account.action_invoice_tree")
res = self.env.ref('account.invoice_form', False) res = self.env.ref("account.invoice_form", False)
result = action.read()[0] result = action.read()[0]
invoice_ids = self.mapped('rma_line_ids.invoice_id').ids invoice_ids = self.mapped("rma_line_ids.invoice_id").ids
if invoice_ids: if invoice_ids:
# choose the view_mode accordingly # choose the view_mode accordingly
if len(invoice_ids) > 1: if len(invoice_ids) > 1:
result['domain'] = [('id', 'in', invoice_ids)] result["domain"] = [("id", "in", invoice_ids)]
else: else:
result['views'] = [(res and res.id or False, 'form')] result["views"] = [(res and res.id or False, "form")]
result['res_id'] = invoice_ids[0] result["res_id"] = invoice_ids[0]
return result return result

View File

@@ -1,8 +1,9 @@
# © 2017 Eficent Business and IT Consulting Services S.L. # © 2017 Eficent Business and IT Consulting Services S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html)
from odoo import api, fields, models, _ from odoo import _, api, fields, models
from odoo.exceptions import ValidationError, UserError from odoo.exceptions import UserError, ValidationError
from odoo.addons import decimal_precision as dp from odoo.addons import decimal_precision as dp
@@ -11,90 +12,125 @@ class RmaOrderLine(models.Model):
@api.model @api.model
def _default_invoice_address(self): def _default_invoice_address(self):
partner_id = self.env.context.get('partner_id') partner_id = self.env.context.get("partner_id")
if partner_id: if partner_id:
return self.env['res.partner'].browse(partner_id) return self.env["res.partner"].browse(partner_id)
return self.env['res.partner'] return self.env["res.partner"]
@api.multi @api.multi
@api.depends('refund_line_ids', 'refund_line_ids.invoice_id.state', @api.depends(
'refund_policy', 'type') "refund_line_ids", "refund_line_ids.invoice_id.state", "refund_policy", "type"
)
def _compute_qty_refunded(self): def _compute_qty_refunded(self):
for rec in self: for rec in self:
rec.qty_refunded = sum(rec.refund_line_ids.filtered( rec.qty_refunded = sum(
lambda i: i.invoice_id.state in ('open', 'paid')).mapped( rec.refund_line_ids.filtered(
'quantity')) lambda i: i.invoice_id.state in ("open", "paid")
).mapped("quantity")
)
@api.depends('refund_line_ids', 'refund_line_ids.invoice_id.state', @api.depends(
'refund_policy', 'move_ids', 'move_ids.state', 'type') "refund_line_ids",
"refund_line_ids.invoice_id.state",
"refund_policy",
"move_ids",
"move_ids.state",
"type",
)
def _compute_qty_to_refund(self): def _compute_qty_to_refund(self):
qty = 0.0 qty = 0.0
for res in self: for res in self:
if res.refund_policy == 'ordered': if res.refund_policy == "ordered":
qty = res.product_qty - res.qty_refunded qty = res.product_qty - res.qty_refunded
elif res.refund_policy == 'received': elif res.refund_policy == "received":
qty = res.qty_received - res.qty_refunded qty = res.qty_received - res.qty_refunded
elif res.refund_policy == 'delivered': elif res.refund_policy == "delivered":
qty = res.qty_delivered - res.qty_refunded qty = res.qty_delivered - res.qty_refunded
res.qty_to_refund = qty res.qty_to_refund = qty
@api.multi @api.multi
def _compute_refund_count(self): def _compute_refund_count(self):
for rec in self: for rec in self:
rec.refund_count = len(rec.refund_line_ids.mapped('invoice_id')) rec.refund_count = len(rec.refund_line_ids.mapped("invoice_id"))
invoice_address_id = fields.Many2one( invoice_address_id = fields.Many2one(
'res.partner', string='Partner invoice address', "res.partner",
string="Partner invoice address",
default=lambda self: self._default_invoice_address(), default=lambda self: self._default_invoice_address(),
readonly=True, states={'draft': [('readonly', False)]}, readonly=True,
states={"draft": [("readonly", False)]},
help="Invoice address for current rma order.", help="Invoice address for current rma order.",
) )
refund_count = fields.Integer( refund_count = fields.Integer(
compute='_compute_refund_count', string='# of Refunds', default=0) compute="_compute_refund_count", string="# of Refunds", default=0
)
invoice_line_id = fields.Many2one( invoice_line_id = fields.Many2one(
comodel_name='account.invoice.line', comodel_name="account.invoice.line",
string='Originating Invoice Line', string="Originating Invoice Line",
ondelete='restrict', ondelete="restrict",
index=True, index=True,
readonly=True, states={'draft': [('readonly', False)]}, readonly=True,
states={"draft": [("readonly", False)]},
) )
refund_line_ids = fields.One2many( refund_line_ids = fields.One2many(
comodel_name='account.invoice.line', comodel_name="account.invoice.line",
inverse_name='rma_line_id', string='Refund Lines', inverse_name="rma_line_id",
copy=False, index=True, readonly=True, string="Refund Lines",
copy=False,
index=True,
readonly=True,
) )
invoice_id = fields.Many2one('account.invoice', string='Source', invoice_id = fields.Many2one(
related='invoice_line_id.invoice_id', "account.invoice",
index=True, readonly=True) string="Source",
refund_policy = fields.Selection([ related="invoice_line_id.invoice_id",
('no', 'No refund'), ('ordered', 'Based on Ordered Quantities'), index=True,
('delivered', 'Based on Delivered Quantities'), readonly=True,
('received', 'Based on Received Quantities')], string="Refund Policy", )
required=True, default='no', refund_policy = fields.Selection(
readonly=True, states={'draft': [('readonly', False)]}, [
("no", "No refund"),
("ordered", "Based on Ordered Quantities"),
("delivered", "Based on Delivered Quantities"),
("received", "Based on Received Quantities"),
],
string="Refund Policy",
required=True,
default="no",
readonly=True,
states={"draft": [("readonly", False)]},
) )
qty_to_refund = fields.Float( qty_to_refund = fields.Float(
string='Qty To Refund', copy=False, string="Qty To Refund",
digits=dp.get_precision('Product Unit of Measure'), readonly=True, copy=False,
compute='_compute_qty_to_refund', store=True) digits=dp.get_precision("Product Unit of Measure"),
readonly=True,
compute="_compute_qty_to_refund",
store=True,
)
qty_refunded = fields.Float( qty_refunded = fields.Float(
string='Qty Refunded', copy=False, string="Qty Refunded",
digits=dp.get_precision('Product Unit of Measure'), copy=False,
readonly=True, compute='_compute_qty_refunded', store=True) digits=dp.get_precision("Product Unit of Measure"),
readonly=True,
compute="_compute_qty_refunded",
store=True,
)
@api.onchange('product_id', 'partner_id') @api.onchange("product_id", "partner_id")
def _onchange_product_id(self): def _onchange_product_id(self):
"""Domain for sale_line_id is computed here to make it dynamic.""" """Domain for sale_line_id is computed here to make it dynamic."""
res = super(RmaOrderLine, self)._onchange_product_id() res = super(RmaOrderLine, self)._onchange_product_id()
if not res.get('domain'): if not res.get("domain"):
res['domain'] = {} res["domain"] = {}
domain = [ domain = [
'|', "|",
('invoice_id.partner_id', '=', self.partner_id.id), ("invoice_id.partner_id", "=", self.partner_id.id),
('invoice_id.partner_id', 'child_of', self.partner_id.id)] ("invoice_id.partner_id", "child_of", self.partner_id.id),
]
if self.product_id: if self.product_id:
domain.append(('product_id', '=', self.product_id.id)) domain.append(("product_id", "=", self.product_id.id))
res['domain']['invoice_line_id'] = domain res["domain"]["invoice_line_id"] = domain
return res return res
@api.multi @api.multi
@@ -102,137 +138,157 @@ class RmaOrderLine(models.Model):
self.ensure_one() self.ensure_one()
if not self.type: if not self.type:
self.type = self._get_default_type() self.type = self._get_default_type()
if self.type == 'customer': if self.type == "customer":
operation = line.product_id.rma_customer_operation_id or \ operation = (
line.product_id.categ_id.rma_customer_operation_id line.product_id.rma_customer_operation_id
or line.product_id.categ_id.rma_customer_operation_id
)
else: else:
operation = line.product_id.rma_supplier_operation_id or \ operation = (
line.product_id.categ_id.rma_supplier_operation_id line.product_id.rma_supplier_operation_id
or line.product_id.categ_id.rma_supplier_operation_id
)
if not operation: if not operation:
operation = self.env['rma.operation'].search( operation = self.env["rma.operation"].search(
[('type', '=', self.type)], limit=1) [("type", "=", self.type)], limit=1
)
if not operation: if not operation:
raise ValidationError(_("Please define an operation first")) raise ValidationError(_("Please define an operation first"))
if not operation.in_route_id or not operation.out_route_id: if not operation.in_route_id or not operation.out_route_id:
route = self.env['stock.location.route'].search( route = self.env["stock.location.route"].search(
[('rma_selectable', '=', True)], limit=1) [("rma_selectable", "=", True)], limit=1
)
if not route: if not route:
raise ValidationError(_("Please define an rma route")) raise ValidationError(_("Please define an rma route"))
if not operation.in_warehouse_id or not operation.out_warehouse_id: if not operation.in_warehouse_id or not operation.out_warehouse_id:
warehouse = self.env['stock.warehouse'].search( warehouse = self.env["stock.warehouse"].search(
[('company_id', '=', self.company_id.id), [("company_id", "=", self.company_id.id), ("lot_rma_id", "!=", False)],
('lot_rma_id', '!=', False)], limit=1) limit=1,
)
if not warehouse: if not warehouse:
raise ValidationError(_("Please define a warehouse with a" raise ValidationError(
" default rma location")) _("Please define a warehouse with a" " default rma location")
)
data = { data = {
'product_id': line.product_id.id, "product_id": line.product_id.id,
'origin': line.invoice_id.number, "origin": line.invoice_id.number,
'uom_id': line.uom_id.id, "uom_id": line.uom_id.id,
'operation_id': operation.id, "operation_id": operation.id,
'product_qty': line.quantity, "product_qty": line.quantity,
'price_unit': line.invoice_id.currency_id.compute( "price_unit": line.invoice_id.currency_id.compute(
line.price_unit, line.currency_id, round=False), line.price_unit, line.currency_id, round=False
'delivery_address_id': line.invoice_id.partner_id.id, ),
'invoice_address_id': line.invoice_id.partner_id.id, "delivery_address_id": line.invoice_id.partner_id.id,
'receipt_policy': operation.receipt_policy, "invoice_address_id": line.invoice_id.partner_id.id,
'refund_policy': operation.refund_policy, "receipt_policy": operation.receipt_policy,
'delivery_policy': operation.delivery_policy, "refund_policy": operation.refund_policy,
'currency_id': line.currency_id.id, "delivery_policy": operation.delivery_policy,
'in_warehouse_id': operation.in_warehouse_id.id or warehouse.id, "currency_id": line.currency_id.id,
'out_warehouse_id': operation.out_warehouse_id.id or warehouse.id, "in_warehouse_id": operation.in_warehouse_id.id or warehouse.id,
'in_route_id': operation.in_route_id.id or route.id, "out_warehouse_id": operation.out_warehouse_id.id or warehouse.id,
'out_route_id': operation.out_route_id.id or route.id, "in_route_id": operation.in_route_id.id or route.id,
'location_id': (operation.location_id.id or "out_route_id": operation.out_route_id.id or route.id,
operation.in_warehouse_id.lot_rma_id.id or "location_id": (
warehouse.lot_rma_id.id), operation.location_id.id
or operation.in_warehouse_id.lot_rma_id.id
or warehouse.lot_rma_id.id
),
} }
return data return data
@api.onchange('invoice_line_id') @api.onchange("invoice_line_id")
def _onchange_invoice_line_id(self): def _onchange_invoice_line_id(self):
if not self.invoice_line_id: if not self.invoice_line_id:
return return
data = self._prepare_rma_line_from_inv_line( data = self._prepare_rma_line_from_inv_line(self.invoice_line_id)
self.invoice_line_id)
self.update(data) self.update(data)
self._remove_other_data_origin('invoice_line_id') self._remove_other_data_origin("invoice_line_id")
@api.multi @api.multi
@api.constrains('invoice_line_id', 'partner_id') @api.constrains("invoice_line_id", "partner_id")
def _check_invoice_partner(self): def _check_invoice_partner(self):
for rec in self: for rec in self:
if (rec.invoice_line_id and if (
rec.invoice_line_id.invoice_id.partner_id != rec.invoice_line_id
rec.partner_id): and rec.invoice_line_id.invoice_id.partner_id != rec.partner_id
raise ValidationError(_( ):
"RMA customer and originating invoice line customer " raise ValidationError(
"doesn't match.")) _(
"RMA customer and originating invoice line customer "
"doesn't match."
)
)
@api.multi @api.multi
def _remove_other_data_origin(self, exception): def _remove_other_data_origin(self, exception):
res = super(RmaOrderLine, self)._remove_other_data_origin(exception) res = super(RmaOrderLine, self)._remove_other_data_origin(exception)
if not exception == 'invoice_line_id': if not exception == "invoice_line_id":
self.invoice_line_id = False self.invoice_line_id = False
return res return res
@api.onchange('operation_id') @api.onchange("operation_id")
def _onchange_operation_id(self): def _onchange_operation_id(self):
result = super(RmaOrderLine, self)._onchange_operation_id() result = super(RmaOrderLine, self)._onchange_operation_id()
if self.operation_id: if self.operation_id:
self.refund_policy = self.operation_id.refund_policy or 'no' self.refund_policy = self.operation_id.refund_policy or "no"
return result return result
@api.multi @api.multi
@api.constrains('invoice_line_id') @api.constrains("invoice_line_id")
def _check_duplicated_lines(self): def _check_duplicated_lines(self):
for line in self: for line in self:
matching_inv_lines = self.env['account.invoice.line'].search([( matching_inv_lines = self.env["account.invoice.line"].search(
'id', '=', line.invoice_line_id.id)]) [("id", "=", line.invoice_line_id.id)]
)
if len(matching_inv_lines) > 1: if len(matching_inv_lines) > 1:
raise UserError( raise UserError(
_("There's an rma for the invoice line %s " _(
"and invoice %s" % "There's an rma for the invoice line %s "
(line.invoice_line_id, "and invoice %s"
line.invoice_line_id.invoice_id))) % (line.invoice_line_id, line.invoice_line_id.invoice_id)
)
)
return {} return {}
@api.multi @api.multi
def action_view_invoice(self): def action_view_invoice(self):
action = self.env.ref('account.action_invoice_tree') action = self.env.ref("account.action_invoice_tree")
result = action.read()[0] result = action.read()[0]
res = self.env.ref('account.invoice_form', False) res = self.env.ref("account.invoice_form", False)
result['views'] = [(res and res.id or False, 'form')] result["views"] = [(res and res.id or False, "form")]
result['view_id'] = res and res.id or False result["view_id"] = res and res.id or False
result['res_id'] = self.invoice_line_id.invoice_id.id result["res_id"] = self.invoice_line_id.invoice_id.id
return result return result
@api.multi @api.multi
def action_view_refunds(self): def action_view_refunds(self):
action = self.env.ref('account.action_invoice_tree2') action = self.env.ref("account.action_invoice_tree2")
result = action.read()[0] result = action.read()[0]
invoice_ids = self.mapped('refund_line_ids.invoice_id').ids invoice_ids = self.mapped("refund_line_ids.invoice_id").ids
if invoice_ids: if invoice_ids:
# choose the view_mode accordingly # choose the view_mode accordingly
if len(invoice_ids) > 1: if len(invoice_ids) > 1:
result['domain'] = [('id', 'in', invoice_ids)] result["domain"] = [("id", "in", invoice_ids)]
else: else:
res = self.env.ref('account.invoice_supplier_form', False) res = self.env.ref("account.invoice_supplier_form", False)
result['views'] = [(res and res.id or False, 'form')] result["views"] = [(res and res.id or False, "form")]
result['res_id'] = invoice_ids[0] result["res_id"] = invoice_ids[0]
return result return result
@api.multi @api.multi
def name_get(self): def name_get(self):
res = [] res = []
if self.env.context.get('rma'): if self.env.context.get("rma"):
for rma in self: for rma in self:
res.append((rma.id, "%s %s qty:%s" % ( res.append(
rma.name, (
rma.product_id.name, rma.id,
rma.product_qty))) "%s %s qty:%s"
% (rma.name, rma.product_id.name, rma.product_qty),
)
)
return res return res
else: else:
return super(RmaOrderLine, self).name_get() return super(RmaOrderLine, self).name_get()

View File

@@ -5,193 +5,206 @@ from odoo.tests import common
class TestRmaAccount(common.SingleTransactionCase): class TestRmaAccount(common.SingleTransactionCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
super(TestRmaAccount, cls).setUpClass() super(TestRmaAccount, cls).setUpClass()
cls.rma_obj = cls.env['rma.order'] cls.rma_obj = cls.env["rma.order"]
cls.rma_line_obj = cls.env['rma.order.line'] cls.rma_line_obj = cls.env["rma.order.line"]
cls.rma_op_obj = cls.env['rma.operation'] cls.rma_op_obj = cls.env["rma.operation"]
cls.rma_add_invoice_wiz = cls.env['rma_add_invoice'] cls.rma_add_invoice_wiz = cls.env["rma_add_invoice"]
cls.rma_refund_wiz = cls.env['rma.refund'] cls.rma_refund_wiz = cls.env["rma.refund"]
cls.acc_obj = cls.env['account.account'] cls.acc_obj = cls.env["account.account"]
cls.inv_obj = cls.env['account.invoice'] cls.inv_obj = cls.env["account.invoice"]
cls.invl_obj = cls.env['account.invoice.line'] cls.invl_obj = cls.env["account.invoice.line"]
cls.product_obj = cls.env['product.product'] cls.product_obj = cls.env["product.product"]
cls.partner_obj = cls.env['res.partner'] cls.partner_obj = cls.env["res.partner"]
cls.rma_route_cust = cls.env.ref('rma.route_rma_customer') cls.rma_route_cust = cls.env.ref("rma.route_rma_customer")
receivable_type = cls.env.ref('account.data_account_type_receivable') receivable_type = cls.env.ref("account.data_account_type_receivable")
payable_type = cls.env.ref('account.data_account_type_payable') payable_type = cls.env.ref("account.data_account_type_payable")
cls.cust_refund_op = cls.env.ref( cls.cust_refund_op = cls.env.ref("rma_account.rma_operation_customer_refund")
'rma_account.rma_operation_customer_refund')
# Create partners # Create partners
customer1 = cls.partner_obj.create({'name': 'Customer 1'}) customer1 = cls.partner_obj.create({"name": "Customer 1"})
supplier1 = cls.partner_obj.create({'name': 'Supplier 1'}) supplier1 = cls.partner_obj.create({"name": "Supplier 1"})
# Create RMA group and operation: # Create RMA group and operation:
cls.rma_group_customer = cls.rma_obj.create({ cls.rma_group_customer = cls.rma_obj.create(
'partner_id': customer1.id, {"partner_id": customer1.id, "type": "customer"}
'type': 'customer', )
}) cls.rma_group_supplier = cls.rma_obj.create(
cls.rma_group_supplier = cls.rma_obj.create({ {"partner_id": supplier1.id, "type": "supplier"}
'partner_id': supplier1.id, )
'type': 'supplier', cls.operation_1 = cls.rma_op_obj.create(
}) {
cls.operation_1 = cls.rma_op_obj.create({ "code": "TEST",
'code': 'TEST', "name": "Refund and receive",
'name': 'Refund and receive', "type": "customer",
'type': 'customer', "receipt_policy": "ordered",
'receipt_policy': 'ordered', "refund_policy": "ordered",
'refund_policy': 'ordered', "in_route_id": cls.rma_route_cust.id,
'in_route_id': cls.rma_route_cust.id, "out_route_id": cls.rma_route_cust.id,
'out_route_id': cls.rma_route_cust.id, }
}) )
# Create products # Create products
cls.product_1 = cls.product_obj.create({ cls.product_1 = cls.product_obj.create(
'name': 'Test Product 1', {
'type': 'product', "name": "Test Product 1",
'list_price': 100.0, "type": "product",
'rma_customer_operation_id': cls.cust_refund_op.id, "list_price": 100.0,
}) "rma_customer_operation_id": cls.cust_refund_op.id,
cls.product_2 = cls.product_obj.create({ }
'name': 'Test Product 2', )
'type': 'product', cls.product_2 = cls.product_obj.create(
'list_price': 150.0, {
'rma_customer_operation_id': cls.operation_1.id, "name": "Test Product 2",
}) "type": "product",
cls.product_3 = cls.product_obj.create({ "list_price": 150.0,
'name': 'Test Product 3', "rma_customer_operation_id": cls.operation_1.id,
'type': 'product', }
}) )
cls.product_4 = cls.product_obj.create({ cls.product_3 = cls.product_obj.create(
'name': 'Test Product 4', {"name": "Test Product 3", "type": "product"}
'type': 'product', )
}) cls.product_4 = cls.product_obj.create(
{"name": "Test Product 4", "type": "product"}
)
# Create Invoices: # Create Invoices:
customer_account = cls.acc_obj. search( customer_account = cls.acc_obj.search(
[('user_type_id', '=', receivable_type.id)], limit=1).id [("user_type_id", "=", receivable_type.id)], limit=1
cls.inv_customer = cls.inv_obj.create({ ).id
'partner_id': customer1.id, cls.inv_customer = cls.inv_obj.create(
'account_id': customer_account, {
'type': 'out_invoice', "partner_id": customer1.id,
}) "account_id": customer_account,
cls.inv_line_1 = cls.invl_obj.create({ "type": "out_invoice",
'name': cls.product_1.name, }
'product_id': cls.product_1.id, )
'quantity': 12.0, cls.inv_line_1 = cls.invl_obj.create(
'price_unit': 100.0, {
'invoice_id': cls.inv_customer.id, "name": cls.product_1.name,
'uom_id': cls.product_1.uom_id.id, "product_id": cls.product_1.id,
'account_id': customer_account, "quantity": 12.0,
}) "price_unit": 100.0,
cls.inv_line_2 = cls.invl_obj.create({ "invoice_id": cls.inv_customer.id,
'name': cls.product_2.name, "uom_id": cls.product_1.uom_id.id,
'product_id': cls.product_2.id, "account_id": customer_account,
'quantity': 15.0, }
'price_unit': 150.0, )
'invoice_id': cls.inv_customer.id, cls.inv_line_2 = cls.invl_obj.create(
'uom_id': cls.product_2.uom_id.id, {
'account_id': customer_account, "name": cls.product_2.name,
}) "product_id": cls.product_2.id,
"quantity": 15.0,
"price_unit": 150.0,
"invoice_id": cls.inv_customer.id,
"uom_id": cls.product_2.uom_id.id,
"account_id": customer_account,
}
)
supplier_account = cls.acc_obj.search( supplier_account = cls.acc_obj.search(
[('user_type_id', '=', payable_type.id)], limit=1).id [("user_type_id", "=", payable_type.id)], limit=1
cls.inv_supplier = cls.inv_obj.create({ ).id
'partner_id': supplier1.id, cls.inv_supplier = cls.inv_obj.create(
'account_id': supplier_account, {
'type': 'in_invoice', "partner_id": supplier1.id,
}) "account_id": supplier_account,
cls.inv_line_3 = cls.invl_obj.create({ "type": "in_invoice",
'name': cls.product_3.name, }
'product_id': cls.product_3.id, )
'quantity': 17.0, cls.inv_line_3 = cls.invl_obj.create(
'price_unit': 250.0, {
'invoice_id': cls.inv_supplier.id, "name": cls.product_3.name,
'uom_id': cls.product_3.uom_id.id, "product_id": cls.product_3.id,
'account_id': supplier_account, "quantity": 17.0,
}) "price_unit": 250.0,
cls.inv_line_4 = cls.invl_obj.create({ "invoice_id": cls.inv_supplier.id,
'name': cls.product_4.name, "uom_id": cls.product_3.uom_id.id,
'product_id': cls.product_4.id, "account_id": supplier_account,
'quantity': 9.0, }
'price_unit': 300.0, )
'invoice_id': cls.inv_supplier.id, cls.inv_line_4 = cls.invl_obj.create(
'uom_id': cls.product_4.uom_id.id, {
'account_id': supplier_account, "name": cls.product_4.name,
}) "product_id": cls.product_4.id,
"quantity": 9.0,
"price_unit": 300.0,
"invoice_id": cls.inv_supplier.id,
"uom_id": cls.product_4.uom_id.id,
"account_id": supplier_account,
}
)
def test_01_add_from_invoice_customer(self): def test_01_add_from_invoice_customer(self):
"""Test wizard to create RMA from a customer invoice.""" """Test wizard to create RMA from a customer invoice."""
add_inv = self.rma_add_invoice_wiz.with_context({ add_inv = self.rma_add_invoice_wiz.with_context(
'customer': True, {
'active_ids': self.rma_group_customer.id, "customer": True,
'active_model': 'rma.order', "active_ids": self.rma_group_customer.id,
}).create({ "active_model": "rma.order",
'invoice_line_ids': }
[(6, 0, self.inv_customer.invoice_line_ids.ids)], ).create({"invoice_line_ids": [(6, 0, self.inv_customer.invoice_line_ids.ids)]})
})
add_inv.add_lines() add_inv.add_lines()
self.assertEqual(len(self.rma_group_customer.rma_line_ids), 2) self.assertEqual(len(self.rma_group_customer.rma_line_ids), 2)
for t in self.rma_group_supplier.rma_line_ids.mapped('type'): for t in self.rma_group_supplier.rma_line_ids.mapped("type"):
self.assertEqual(t, 'customer') self.assertEqual(t, "customer")
rma_1 = self.rma_group_customer.rma_line_ids.filtered( rma_1 = self.rma_group_customer.rma_line_ids.filtered(
lambda r: r.product_id == self.product_1) lambda r: r.product_id == self.product_1
)
self.assertEqual(rma_1.operation_id, self.cust_refund_op) self.assertEqual(rma_1.operation_id, self.cust_refund_op)
rma_2 = self.rma_group_customer.rma_line_ids.filtered( rma_2 = self.rma_group_customer.rma_line_ids.filtered(
lambda r: r.product_id == self.product_2) lambda r: r.product_id == self.product_2
)
self.assertEqual(rma_2.operation_id, self.operation_1) self.assertEqual(rma_2.operation_id, self.operation_1)
def test_02_add_from_invoice_supplier(self): def test_02_add_from_invoice_supplier(self):
"""Test wizard to create RMA from a vendor bill.""" """Test wizard to create RMA from a vendor bill."""
add_inv = self.rma_add_invoice_wiz.with_context({ add_inv = self.rma_add_invoice_wiz.with_context(
'supplier': True, {
'active_ids': self.rma_group_supplier.id, "supplier": True,
'active_model': 'rma.order', "active_ids": self.rma_group_supplier.id,
}).create({ "active_model": "rma.order",
'invoice_line_ids': }
[(6, 0, self.inv_supplier.invoice_line_ids.ids)], ).create({"invoice_line_ids": [(6, 0, self.inv_supplier.invoice_line_ids.ids)]})
})
add_inv.add_lines() add_inv.add_lines()
self.assertEqual(len(self.rma_group_supplier.rma_line_ids), 2) self.assertEqual(len(self.rma_group_supplier.rma_line_ids), 2)
for t in self.rma_group_supplier.rma_line_ids.mapped('type'): for t in self.rma_group_supplier.rma_line_ids.mapped("type"):
self.assertEqual(t, 'supplier') self.assertEqual(t, "supplier")
def test_03_rma_refund_operation(self): def test_03_rma_refund_operation(self):
"""Test RMA quantities using refund operations.""" """Test RMA quantities using refund operations."""
# Received refund_policy: # Received refund_policy:
rma_1 = self.rma_group_customer.rma_line_ids.filtered( rma_1 = self.rma_group_customer.rma_line_ids.filtered(
lambda r: r.product_id == self.product_1) lambda r: r.product_id == self.product_1
self.assertEqual(rma_1.refund_policy, 'received') )
self.assertEqual(rma_1.refund_policy, "received")
self.assertEqual(rma_1.qty_to_refund, 0.0) self.assertEqual(rma_1.qty_to_refund, 0.0)
# TODO: receive and check qty_to_refund is 12.0 # TODO: receive and check qty_to_refund is 12.0
# Ordered refund_policy: # Ordered refund_policy:
rma_2 = self.rma_group_customer.rma_line_ids.filtered( rma_2 = self.rma_group_customer.rma_line_ids.filtered(
lambda r: r.product_id == self.product_2) lambda r: r.product_id == self.product_2
)
rma_2._onchange_operation_id() rma_2._onchange_operation_id()
self.assertEqual(rma_2.refund_policy, 'ordered') self.assertEqual(rma_2.refund_policy, "ordered")
self.assertEqual(rma_2.qty_to_refund, 15.0) self.assertEqual(rma_2.qty_to_refund, 15.0)
def test_04_rma_create_refund(self): def test_04_rma_create_refund(self):
"""Generate a Refund from a customer RMA.""" """Generate a Refund from a customer RMA."""
rma = self.rma_group_customer.rma_line_ids.filtered( rma = self.rma_group_customer.rma_line_ids.filtered(
lambda r: r.product_id == self.product_2) lambda r: r.product_id == self.product_2
)
rma.action_rma_to_approve() rma.action_rma_to_approve()
rma.action_rma_approve() rma.action_rma_approve()
self.assertEqual(rma.refund_count, 0) self.assertEqual(rma.refund_count, 0)
self.assertEqual(rma.qty_to_refund, 15.0) self.assertEqual(rma.qty_to_refund, 15.0)
self.assertEqual(rma.qty_refunded, 0.0) self.assertEqual(rma.qty_refunded, 0.0)
make_refund = self.rma_refund_wiz.with_context({ make_refund = self.rma_refund_wiz.with_context(
'customer': True, {"customer": True, "active_ids": rma.ids, "active_model": "rma.order.line"}
'active_ids': rma.ids, ).create({"description": "Test refund"})
'active_model': 'rma.order.line',
}).create({
'description': 'Test refund',
})
make_refund.invoice_refund() make_refund.invoice_refund()
rma.refund_line_ids.invoice_id.action_invoice_open() rma.refund_line_ids.invoice_id.action_invoice_open()
rma._compute_refund_count() rma._compute_refund_count()
@@ -201,10 +214,12 @@ class TestRmaAccount(common.SingleTransactionCase):
def test_05_fill_rma_from_inv_line(self): def test_05_fill_rma_from_inv_line(self):
"""Test filling a RMA (line) from a invoice line.""" """Test filling a RMA (line) from a invoice line."""
rma = self.rma_line_obj.new({ rma = self.rma_line_obj.new(
'partner_id': self.inv_customer.partner_id.id, {
'invoice_line_id': self.inv_line_1.id, "partner_id": self.inv_customer.partner_id.id,
}) "invoice_line_id": self.inv_line_1.id,
}
)
self.assertFalse(rma.product_id) self.assertFalse(rma.product_id)
rma._onchange_invoice_line_id() rma._onchange_invoice_line_id()
self.assertEqual(rma.product_id, self.product_1) self.assertEqual(rma.product_id, self.product_1)

View File

@@ -6,90 +6,106 @@ from odoo.exceptions import ValidationError
class RmaAddInvoice(models.TransientModel): class RmaAddInvoice(models.TransientModel):
_name = 'rma_add_invoice' _name = "rma_add_invoice"
_description = 'Wizard to add rma lines' _description = "Wizard to add rma lines"
@api.model @api.model
def default_get(self, fields_list): def default_get(self, fields_list):
res = super(RmaAddInvoice, self).default_get(fields_list) res = super(RmaAddInvoice, self).default_get(fields_list)
rma_obj = self.env['rma.order'] rma_obj = self.env["rma.order"]
rma_id = self.env.context['active_ids'] or [] rma_id = self.env.context["active_ids"] or []
active_model = self.env.context['active_model'] active_model = self.env.context["active_model"]
if not rma_id: if not rma_id:
return res return res
assert active_model == 'rma.order', 'Bad context propagation' assert active_model == "rma.order", "Bad context propagation"
rma = rma_obj.browse(rma_id) rma = rma_obj.browse(rma_id)
res['rma_id'] = rma.id res["rma_id"] = rma.id
res['partner_id'] = rma.partner_id.id res["partner_id"] = rma.partner_id.id
res['invoice_line_ids'] = False res["invoice_line_ids"] = False
return res return res
rma_id = fields.Many2one('rma.order', string='RMA Order', readonly=True, rma_id = fields.Many2one(
ondelete='cascade') "rma.order", string="RMA Order", readonly=True, ondelete="cascade"
partner_id = fields.Many2one(comodel_name='res.partner', string='Partner', )
readonly=True) partner_id = fields.Many2one(
invoice_line_ids = fields.Many2many('account.invoice.line', comodel_name="res.partner", string="Partner", readonly=True
'rma_add_invoice_add_line_rel', )
'invoice_line_id', invoice_line_ids = fields.Many2many(
'rma_add_invoice_id', "account.invoice.line",
string='Invoice Lines') "rma_add_invoice_add_line_rel",
"invoice_line_id",
"rma_add_invoice_id",
string="Invoice Lines",
)
def _prepare_rma_line_from_inv_line(self, line): def _prepare_rma_line_from_inv_line(self, line):
if self.env.context.get('customer'): if self.env.context.get("customer"):
operation = line.product_id.rma_customer_operation_id or \ operation = (
line.product_id.categ_id.rma_customer_operation_id line.product_id.rma_customer_operation_id
or line.product_id.categ_id.rma_customer_operation_id
)
else: else:
operation = line.product_id.rma_supplier_operation_id or \ operation = (
line.product_id.categ_id.rma_supplier_operation_id line.product_id.rma_supplier_operation_id
or line.product_id.categ_id.rma_supplier_operation_id
)
if not operation: if not operation:
operation = self.env['rma.operation'].search( operation = self.env["rma.operation"].search(
[('type', '=', self.rma_id.type)], limit=1) [("type", "=", self.rma_id.type)], limit=1
)
if not operation: if not operation:
raise ValidationError(_("Please define an operation first")) raise ValidationError(_("Please define an operation first"))
if not operation.in_route_id or not operation.out_route_id: if not operation.in_route_id or not operation.out_route_id:
route = self.env['stock.location.route'].search( route = self.env["stock.location.route"].search(
[('rma_selectable', '=', True)], limit=1) [("rma_selectable", "=", True)], limit=1
)
if not route: if not route:
raise ValidationError(_("Please define an rma route")) raise ValidationError(_("Please define an rma route"))
if not operation.in_warehouse_id or not operation.out_warehouse_id: if not operation.in_warehouse_id or not operation.out_warehouse_id:
warehouse = self.env['stock.warehouse'].search( warehouse = self.env["stock.warehouse"].search(
[('company_id', '=', self.rma_id.company_id.id), [
('lot_rma_id', '!=', False)], limit=1) ("company_id", "=", self.rma_id.company_id.id),
("lot_rma_id", "!=", False),
],
limit=1,
)
if not warehouse: if not warehouse:
raise ValidationError(_("Please define a warehouse with a" raise ValidationError(
" default rma location")) _("Please define a warehouse with a" " default rma location")
)
data = { data = {
'partner_id': self.partner_id.id, "partner_id": self.partner_id.id,
'invoice_line_id': line.id, "invoice_line_id": line.id,
'product_id': line.product_id.id, "product_id": line.product_id.id,
'origin': line.invoice_id.number, "origin": line.invoice_id.number,
'uom_id': line.uom_id.id, "uom_id": line.uom_id.id,
'operation_id': operation.id, "operation_id": operation.id,
'product_qty': line.quantity, "product_qty": line.quantity,
'price_unit': line.invoice_id.currency_id.compute( "price_unit": line.invoice_id.currency_id.compute(
line.price_unit, line.currency_id, round=False), line.price_unit, line.currency_id, round=False
'delivery_address_id': line.invoice_id.partner_id.id, ),
'invoice_address_id': line.invoice_id.partner_id.id, "delivery_address_id": line.invoice_id.partner_id.id,
'rma_id': self.rma_id.id, "invoice_address_id": line.invoice_id.partner_id.id,
'receipt_policy': operation.receipt_policy, "rma_id": self.rma_id.id,
'refund_policy': operation.refund_policy, "receipt_policy": operation.receipt_policy,
'delivery_policy': operation.delivery_policy, "refund_policy": operation.refund_policy,
'in_warehouse_id': operation.in_warehouse_id.id or warehouse.id, "delivery_policy": operation.delivery_policy,
'out_warehouse_id': operation.out_warehouse_id.id or warehouse.id, "in_warehouse_id": operation.in_warehouse_id.id or warehouse.id,
'in_route_id': operation.in_route_id.id or route.id, "out_warehouse_id": operation.out_warehouse_id.id or warehouse.id,
'out_route_id': operation.out_route_id.id or route.id, "in_route_id": operation.in_route_id.id or route.id,
'location_id': (operation.location_id.id or "out_route_id": operation.out_route_id.id or route.id,
operation.in_warehouse_id.lot_rma_id.id or "location_id": (
warehouse.lot_rma_id.id), operation.location_id.id
or operation.in_warehouse_id.lot_rma_id.id
or warehouse.lot_rma_id.id
),
} }
return data return data
@api.model @api.model
def _get_rma_data(self): def _get_rma_data(self):
data = { data = {"date_rma": fields.Datetime.now()}
'date_rma': fields.Datetime.now(),
}
return data return data
@api.model @api.model
@@ -101,7 +117,7 @@ class RmaAddInvoice(models.TransientModel):
@api.multi @api.multi
def add_lines(self): def add_lines(self):
rma_line_obj = self.env['rma.order.line'] rma_line_obj = self.env["rma.order.line"]
existing_invoice_lines = self._get_existing_invoice_lines() existing_invoice_lines = self._get_existing_invoice_lines()
for line in self.invoice_line_ids: for line in self.invoice_line_ids:
# Load a PO line only once # Load a PO line only once
@@ -111,4 +127,4 @@ class RmaAddInvoice(models.TransientModel):
rma = self.rma_id rma = self.rma_id
data_rma = self._get_rma_data() data_rma = self._get_rma_data()
rma.write(data_rma) rma.write(data_rma)
return {'type': 'ir.actions.act_window_close'} return {"type": "ir.actions.act_window_close"}

View File

@@ -9,9 +9,8 @@ class RmaLineMakeSupplierRma(models.TransientModel):
@api.model @api.model
def _prepare_supplier_rma_line(self, rma, item): def _prepare_supplier_rma_line(self, rma, item):
res = super(RmaLineMakeSupplierRma, self)._prepare_supplier_rma_line( res = super(RmaLineMakeSupplierRma, self)._prepare_supplier_rma_line(rma, item)
rma, item) if res["operation_id"]:
if res['operation_id']: operation = self.env["rma.operation"].browse(res["operation_id"])
operation = self.env['rma.operation'].browse(res['operation_id']) res["refund_policy"] = operation.refund_policy
res['refund_policy'] = operation.refund_policy
return res return res

View File

@@ -3,6 +3,7 @@
from odoo import _, api, fields, models from odoo import _, api, fields, models
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
import odoo.addons.decimal_precision as dp import odoo.addons.decimal_precision as dp
@@ -12,22 +13,21 @@ class RmaRefund(models.TransientModel):
@api.model @api.model
def _get_reason(self): def _get_reason(self):
active_ids = self.env.context.get('active_ids', False) active_ids = self.env.context.get("active_ids", False)
return self.env['rma.order.line'].browse( return self.env["rma.order.line"].browse(active_ids[0]).rma_id.name or ""
active_ids[0]).rma_id.name or ''
@api.returns('rma.order.line') @api.returns("rma.order.line")
def _prepare_item(self, line): def _prepare_item(self, line):
values = { values = {
'product_id': line.product_id.id, "product_id": line.product_id.id,
'name': line.name, "name": line.name,
'product_qty': line.product_qty, "product_qty": line.product_qty,
'uom_id': line.uom_id.id, "uom_id": line.uom_id.id,
'qty_to_refund': line.qty_to_refund, "qty_to_refund": line.qty_to_refund,
'refund_policy': line.refund_policy, "refund_policy": line.refund_policy,
'invoice_address_id': line.invoice_address_id.id, "invoice_address_id": line.invoice_address_id.id,
'line_id': line.id, "line_id": line.id,
'rma_id': line.rma_id.id, "rma_id": line.rma_id.id,
} }
return values return values
@@ -39,37 +39,37 @@ class RmaRefund(models.TransientModel):
""" """
context = self._context.copy() context = self._context.copy()
res = super(RmaRefund, self).default_get(fields_list) res = super(RmaRefund, self).default_get(fields_list)
rma_line_obj = self.env['rma.order.line'] rma_line_obj = self.env["rma.order.line"]
rma_line_ids = self.env.context['active_ids'] or [] rma_line_ids = self.env.context["active_ids"] or []
active_model = self.env.context['active_model'] active_model = self.env.context["active_model"]
if not rma_line_ids: if not rma_line_ids:
return res return res
assert active_model == 'rma.order.line', 'Bad context propagation' assert active_model == "rma.order.line", "Bad context propagation"
items = [] items = []
lines = rma_line_obj.browse(rma_line_ids) lines = rma_line_obj.browse(rma_line_ids)
if len(lines.mapped('partner_id')) > 1: if len(lines.mapped("partner_id")) > 1:
raise ValidationError( raise ValidationError(
_("Only RMAs from the same partner can be processed at " _(
"the same time.")) "Only RMAs from the same partner can be processed at "
"the same time."
)
)
for line in lines: for line in lines:
items.append([0, 0, self._prepare_item(line)]) items.append([0, 0, self._prepare_item(line)])
res['item_ids'] = items res["item_ids"] = items
context.update({'items_ids': items}) context.update({"items_ids": items})
return res return res
date_invoice = fields.Date( date_invoice = fields.Date(
string='Refund Date', string="Refund Date", default=fields.Date.context_today, required=True
default=fields.Date.context_today, required=True,
) )
date = fields.Date(string='Accounting Date') date = fields.Date(string="Accounting Date")
description = fields.Char( description = fields.Char(
string='Reason', required=True, string="Reason", required=True, default=lambda self: self._get_reason()
default=lambda self: self._get_reason(),
) )
item_ids = fields.One2many( item_ids = fields.One2many(
comodel_name='rma.refund.item', comodel_name="rma.refund.item", inverse_name="wiz_id", string="Items"
inverse_name='wiz_id', string='Items',
) )
@api.multi @api.multi
@@ -77,102 +77,107 @@ class RmaRefund(models.TransientModel):
for wizard in self: for wizard in self:
first = self.item_ids[0] first = self.item_ids[0]
values = self._prepare_refund(wizard, first.line_id) values = self._prepare_refund(wizard, first.line_id)
new_refund = self.env['account.invoice'].create(values) new_refund = self.env["account.invoice"].create(values)
for item in self.item_ids: for item in self.item_ids:
refund_line_values = self.prepare_refund_line(item, new_refund) refund_line_values = self.prepare_refund_line(item, new_refund)
self.env['account.invoice.line'].create( self.env["account.invoice.line"].create(refund_line_values)
refund_line_values)
return new_refund return new_refund
@api.multi @api.multi
def invoice_refund(self): def invoice_refund(self):
rma_line_ids = self.env['rma.order.line'].browse( rma_line_ids = self.env["rma.order.line"].browse(self.env.context["active_ids"])
self.env.context['active_ids'])
for line in rma_line_ids: for line in rma_line_ids:
if line.refund_policy == 'no': if line.refund_policy == "no":
raise ValidationError( raise ValidationError(
_('The operation is not refund for at least one line')) _("The operation is not refund for at least one line")
if line.state != 'approved': )
raise ValidationError( if line.state != "approved":
_('RMA %s is not approved') % line.name) raise ValidationError(_("RMA %s is not approved") % line.name)
new_invoice = self.compute_refund() new_invoice = self.compute_refund()
action = 'action_invoice_tree1' if ( action = (
new_invoice.type in ['out_refund', 'out_invoice']) \ "action_invoice_tree1"
else 'action_invoice_in_refund' if (new_invoice.type in ["out_refund", "out_invoice"])
result = self.env.ref('account.%s' % action).read()[0] else "action_invoice_in_refund"
form_view = self.env.ref('account.invoice_supplier_form', False) )
result['views'] = [(form_view and form_view.id or False, 'form')] result = self.env.ref("account.%s" % action).read()[0]
result['res_id'] = new_invoice.id form_view = self.env.ref("account.invoice_supplier_form", False)
result["views"] = [(form_view and form_view.id or False, "form")]
result["res_id"] = new_invoice.id
return result return result
@api.model @api.model
def prepare_refund_line(self, item, refund): def prepare_refund_line(self, item, refund):
accounts = item.product_id.product_tmpl_id._get_product_accounts() accounts = item.product_id.product_tmpl_id._get_product_accounts()
if item.line_id.type == 'customer': if item.line_id.type == "customer":
account = accounts['stock_output'] account = accounts["stock_output"]
else: else:
account = accounts['stock_input'] account = accounts["stock_input"]
if not account: if not account:
raise ValidationError(_( raise ValidationError(_("Accounts are not configured for this product."))
"Accounts are not configured for this product."))
values = { values = {
'name': item.line_id.name or item.rma_id.name, "name": item.line_id.name or item.rma_id.name,
'origin': item.line_id.name or item.rma_id.name, "origin": item.line_id.name or item.rma_id.name,
'account_id': account.id, "account_id": account.id,
'price_unit': item.line_id.price_unit, "price_unit": item.line_id.price_unit,
'uom_id': item.line_id.uom_id.id, "uom_id": item.line_id.uom_id.id,
'product_id': item.product_id.id, "product_id": item.product_id.id,
'rma_line_id': item.line_id.id, "rma_line_id": item.line_id.id,
'quantity': item.qty_to_refund, "quantity": item.qty_to_refund,
'invoice_id': refund.id "invoice_id": refund.id,
} }
return values return values
@api.model @api.model
def _prepare_refund(self, wizard, rma_line): def _prepare_refund(self, wizard, rma_line):
# origin_invoices = self._get_invoice(rma_line) # origin_invoices = self._get_invoice(rma_line)
if rma_line.type == 'customer': if rma_line.type == "customer":
journal = self.env['account.journal'].search( journal = self.env["account.journal"].search(
[('type', '=', 'sale')], limit=1) [("type", "=", "sale")], limit=1
)
else: else:
journal = self.env['account.journal'].search( journal = self.env["account.journal"].search(
[('type', '=', 'purchase')], limit=1) [("type", "=", "purchase")], limit=1
)
values = { values = {
'name': rma_line.rma_id.name or rma_line.name, "name": rma_line.rma_id.name or rma_line.name,
'origin': rma_line.rma_id.name or rma_line.name, "origin": rma_line.rma_id.name or rma_line.name,
'reference': False, "reference": False,
# 'account_id': account.id, # 'account_id': account.id,
'journal_id': journal.id, "journal_id": journal.id,
'currency_id': rma_line.partner_id.company_id.currency_id.id, "currency_id": rma_line.partner_id.company_id.currency_id.id,
'payment_term_id': False, "payment_term_id": False,
'fiscal_position_id': "fiscal_position_id": rma_line.partner_id.property_account_position_id.id,
rma_line.partner_id.property_account_position_id.id, "state": "draft",
'state': 'draft', "number": False,
'number': False, "date": wizard.date,
'date': wizard.date, "date_invoice": wizard.date_invoice,
'date_invoice': wizard.date_invoice, "partner_id": rma_line.invoice_address_id.id or rma_line.partner_id.id,
'partner_id': rma_line.invoice_address_id.id or
rma_line.partner_id.id,
} }
if self.env.registry.models.get('crm.team', False): if self.env.registry.models.get("crm.team", False):
team_ids = self.env['crm.team'].search( team_ids = self.env["crm.team"].search(
['|', ('user_id', '=', self.env.uid), [
('member_ids', '=', self.env.uid)], limit=1) "|",
("user_id", "=", self.env.uid),
("member_ids", "=", self.env.uid),
],
limit=1,
)
team_id = team_ids[0] if team_ids else False team_id = team_ids[0] if team_ids else False
if team_id: if team_id:
values['team_id'] = team_id.id values["team_id"] = team_id.id
if rma_line.type == 'customer': if rma_line.type == "customer":
values['type'] = 'out_refund' values["type"] = "out_refund"
else: else:
values['type'] = 'in_refund' values["type"] = "in_refund"
return values return values
@api.constrains('item_ids') @api.constrains("item_ids")
def check_unique_invoice_address_id(self): def check_unique_invoice_address_id(self):
addresses = self.item_ids.mapped('invoice_address_id') addresses = self.item_ids.mapped("invoice_address_id")
if len(addresses) > 1: if len(addresses) > 1:
raise ValidationError(_( raise ValidationError(
"The invoice address must be the same for all the lines.")) _("The invoice address must be the same for all the lines.")
)
return True return True
@@ -180,33 +185,38 @@ class RmaRefundItem(models.TransientModel):
_name = "rma.refund.item" _name = "rma.refund.item"
_description = "RMA Lines to refund" _description = "RMA Lines to refund"
wiz_id = fields.Many2one( wiz_id = fields.Many2one(comodel_name="rma.refund", string="Wizard", required=True)
comodel_name='rma.refund', string='Wizard', required=True) line_id = fields.Many2one(
line_id = fields.Many2one('rma.order.line', "rma.order.line",
string='RMA order Line', string="RMA order Line",
required=True, required=True,
readonly=True, readonly=True,
ondelete='cascade') ondelete="cascade",
rma_id = fields.Many2one('rma.order', )
related='line_id.rma_id', rma_id = fields.Many2one(
string='RMA', "rma.order", related="line_id.rma_id", string="RMA", readonly=True
readonly=True) )
product_id = fields.Many2one('product.product', string='Product') product_id = fields.Many2one("product.product", string="Product")
product = fields.Many2one('product.product', string='Product', product = fields.Many2one("product.product", string="Product", readonly=True)
readonly=True) name = fields.Char(string="Description", required=True)
name = fields.Char(string='Description', required=True)
product_qty = fields.Float( product_qty = fields.Float(
string='Quantity Ordered', copy=False, string="Quantity Ordered",
digits=dp.get_precision('Product Unit of Measure'), copy=False,
readonly=True) digits=dp.get_precision("Product Unit of Measure"),
readonly=True,
)
invoice_address_id = fields.Many2one( invoice_address_id = fields.Many2one(
comodel_name='res.partner', string='Invoice Address') comodel_name="res.partner", string="Invoice Address"
)
qty_to_refund = fields.Float( qty_to_refund = fields.Float(
string='Quantity To Refund', string="Quantity To Refund", digits=dp.get_precision("Product Unit of Measure")
digits=dp.get_precision('Product Unit of Measure')) )
uom_id = fields.Many2one('uom.uom', string='Unit of Measure', uom_id = fields.Many2one("uom.uom", string="Unit of Measure", readonly=True)
readonly=True) refund_policy = fields.Selection(
refund_policy = fields.Selection(selection=[ selection=[
('no', 'Not required'), ('ordered', 'Based on Ordered Quantities'), ("no", "Not required"),
('received', 'Based on Received Quantities')], ("ordered", "Based on Ordered Quantities"),
string="Refund Policy") ("received", "Based on Received Quantities"),
],
string="Refund Policy",
)