From 252d472a20cccbe1fc0a1a06916151a8bce47121 Mon Sep 17 00:00:00 2001 From: jesusVMayor Date: Tue, 3 Nov 2015 18:20:15 +0100 Subject: [PATCH 01/20] [FIX]crm_claim_rma: fix the view crm_claim_rma_form_view to allow creating new categories with the correct model. --- crm_claim_rma/__openerp__.py | 2 +- crm_claim_rma/views/crm_claim_rma.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crm_claim_rma/__openerp__.py b/crm_claim_rma/__openerp__.py index 062d29b8..4a8f0e18 100644 --- a/crm_claim_rma/__openerp__.py +++ b/crm_claim_rma/__openerp__.py @@ -24,7 +24,7 @@ { 'name': 'RMA Claim (Product Return Management)', - 'version': '8.0.1.1.0', + 'version': '8.0.1.2.0', 'category': 'Generic Modules/CRM & SRM', 'description': """ Management of Return Merchandise Authorization (RMA) diff --git a/crm_claim_rma/views/crm_claim_rma.xml b/crm_claim_rma/views/crm_claim_rma.xml index 65f0e54f..8e5d954a 100644 --- a/crm_claim_rma/views/crm_claim_rma.xml +++ b/crm_claim_rma/views/crm_claim_rma.xml @@ -374,7 +374,7 @@ - + From a49ad0a2012f0ab33efebadddb919f22ba8d7524 Mon Sep 17 00:00:00 2001 From: Osval Reyes Date: Thu, 12 Nov 2015 10:22:45 -0430 Subject: [PATCH 02/20] [IMP] improved module crm_claim_rma --- crm_claim_rma/README.rst | 90 +++ crm_claim_rma/__init__.py | 2 + crm_claim_rma/__openerp__.py | 87 +-- .../{crm_claim_rma.xml => crm_case_categ.xml} | 157 +++--- crm_claim_rma/data/crm_case_section.xml | 10 + crm_claim_rma/data/ir_sequence_type.xml | 16 + crm_claim_rma/demo/account_invoice.xml | 14 + crm_claim_rma/demo/account_invoice_line.xml | 22 + crm_claim_rma/demo/claim_line.xml | 28 + crm_claim_rma/demo/crm_claim.xml | 9 + crm_claim_rma/i18n/crm_claim_rma.pot | 48 +- crm_claim_rma/i18n/es.po | 192 ++++++- crm_claim_rma/i18n/es_MX.po | 16 + crm_claim_rma/i18n/es_PA.po | 16 + crm_claim_rma/i18n/es_VE.po | 16 + crm_claim_rma/i18n/fr.po | 36 +- crm_claim_rma/i18n/pt_BR.po | 30 +- crm_claim_rma/models/__init__.py | 34 +- crm_claim_rma/models/account_invoice.py | 30 +- crm_claim_rma/models/account_invoice_line.py | 43 ++ crm_claim_rma/models/claim_line.py | 432 ++++++++++++++ crm_claim_rma/models/crm_claim.py | 198 +++++++ crm_claim_rma/models/crm_claim_rma.py | 527 ------------------ crm_claim_rma/models/invoice_no_date.py | 31 ++ crm_claim_rma/models/product_no_supplier.py | 32 ++ .../models/{stock.py => stock_move.py} | 26 +- crm_claim_rma/models/stock_picking.py | 38 ++ crm_claim_rma/models/substate_substate.py | 40 ++ crm_claim_rma/security/ir.model.access.csv | 14 +- crm_claim_rma/tests/test_picking_creation.py | 51 +- crm_claim_rma/views/claim_line.xml | 196 +++++++ crm_claim_rma/views/crm_claim.xml | 289 ++++++++++ crm_claim_rma/views/crm_claim_rma.xml | 409 -------------- crm_claim_rma/views/res_partner.xml | 10 +- crm_claim_rma/views/stock_view.xml | 41 ++ .../wizards/account_invoice_refund.py | 9 +- crm_claim_rma/wizards/claim_make_picking.py | 21 +- 37 files changed, 2014 insertions(+), 1246 deletions(-) create mode 100644 crm_claim_rma/README.rst rename crm_claim_rma/data/{crm_claim_rma.xml => crm_case_categ.xml} (58%) create mode 100644 crm_claim_rma/data/crm_case_section.xml create mode 100644 crm_claim_rma/data/ir_sequence_type.xml create mode 100644 crm_claim_rma/demo/account_invoice.xml create mode 100644 crm_claim_rma/demo/account_invoice_line.xml create mode 100644 crm_claim_rma/demo/claim_line.xml create mode 100644 crm_claim_rma/demo/crm_claim.xml create mode 100644 crm_claim_rma/i18n/es_MX.po create mode 100644 crm_claim_rma/i18n/es_PA.po create mode 100644 crm_claim_rma/i18n/es_VE.po create mode 100644 crm_claim_rma/models/account_invoice_line.py create mode 100644 crm_claim_rma/models/claim_line.py create mode 100644 crm_claim_rma/models/crm_claim.py delete mode 100644 crm_claim_rma/models/crm_claim_rma.py create mode 100644 crm_claim_rma/models/invoice_no_date.py create mode 100644 crm_claim_rma/models/product_no_supplier.py rename crm_claim_rma/models/{stock.py => stock_move.py} (75%) create mode 100644 crm_claim_rma/models/stock_picking.py create mode 100644 crm_claim_rma/models/substate_substate.py create mode 100644 crm_claim_rma/views/claim_line.xml create mode 100644 crm_claim_rma/views/crm_claim.xml delete mode 100644 crm_claim_rma/views/crm_claim_rma.xml create mode 100644 crm_claim_rma/views/stock_view.xml diff --git a/crm_claim_rma/README.rst b/crm_claim_rma/README.rst new file mode 100644 index 00000000..69a81a3a --- /dev/null +++ b/crm_claim_rma/README.rst @@ -0,0 +1,90 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +==================================================== +Management of Return Merchandise Authorization (RMA) +==================================================== + +This module aims to improve the Claims by adding a way to manage the +product returns. It allows you to create and manage picking from a +claim. It also introduces a new object: the claim lines to better +handle that problematic. One Claim can have several lines that +concern the return of differents products. It's for every of them +that you'll be able to check the warranty (still running or not). + +It mainly contains the following features: + +* product returns (one by one, mass return by invoice) +* warranty control & return address (based on invoice date and product form) +* product picking in / out +* product refund +* access to related customer data (orders, invoices, refunds, picking + in/out) from a claim +* use the OpenERP chatter within team like in opportunity (reply to refer to + the team, not a person) + +Using this module makes the logistic flow of return this way: + +* Returning product goes into Stock or Supplier location with a incoming + shipment (depending on the settings of the supplier info in the + product form) +* You can make a delivery from the RMA to send a new product to the Customer + +Features +-------- + +- New field priority in claim line +- Calculate priority of claim line depending of today date and claim date +- Grouping by priority in claim line + + +For further information, please visit: + +* https://www.odoo.com/forum/help-1 + +Known issues / Roadmap +====================== + +* Currently, the warranty duration used is the one configured on the + products today, not the one which was configured when the product + has been sold. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed feedback +`here `_. + + +Credits +======= + +Contributors: +------------- + +* Emmanuel Samyn +* Sébastien Beau +* Benoît Guillot +* Joel Grand-Guillaume +* Guewen Baconnier +* Yannick Vaucher +* Yanina Aular +* Osval Reyes + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit http://odoo-community.org. diff --git a/crm_claim_rma/__init__.py b/crm_claim_rma/__init__.py index 0c21c9f9..bbe8bffc 100644 --- a/crm_claim_rma/__init__.py +++ b/crm_claim_rma/__init__.py @@ -1,11 +1,13 @@ # -*- coding: utf-8 -*- ############################################################################## # +# Copyright 2015 Vauxoo # Copyright 2015 Eezee-It # Copyright 2013 Camptocamp # Copyright 2009-2013 Akretion, # Author: Emmanuel Samyn, Raphaël Valyi, Sébastien Beau, # Joel Grand-Guillaume +# Osval Reyes, Yanina Aular # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/crm_claim_rma/__openerp__.py b/crm_claim_rma/__openerp__.py index 4a8f0e18..fa3e9aa0 100644 --- a/crm_claim_rma/__openerp__.py +++ b/crm_claim_rma/__openerp__.py @@ -1,11 +1,13 @@ # -*- coding: utf-8 -*- ############################################################################## # +# Copyright 2015 Vauxoo # Copyright 2015 Eezee-It # Copyright 2013 Camptocamp # Copyright 2009-2013 Akretion, # Author: Emmanuel Samyn, Raphaël Valyi, Sébastien Beau, -# Benoît Guillot, Joel Grand-Guillaume +# Benoît Guillot, Joel Grand-Guillaume, +# Osval Reyes, Yanina Aular # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -24,80 +26,43 @@ { 'name': 'RMA Claim (Product Return Management)', - 'version': '8.0.1.2.0', + 'version': '8.0.1.1.0', 'category': 'Generic Modules/CRM & SRM', - 'description': """ -Management of Return Merchandise Authorization (RMA) -==================================================== - -This module aims to improve the Claims by adding a way to manage the -product returns. It allows you to create and manage picking from a -claim. It also introduces a new object: the claim lines to better -handle that problematic. One Claim can have several lines that -concern the return of differents products. It's for every of them -that you'll be able to check the warranty (still running or not). - -It mainly contains the following features: - -* product returns (one by one, mass return by invoice) -* warranty control & return address (based on invoice date and product form) -* product picking in / out -* product refund -* access to related customer data (orders, invoices, refunds, picking - in/out) from a claim -* use the OpenERP chatter within team like in opportunity (reply to refer to - the team, not a person) - -Using this module makes the logistic flow of return this way: - -* Returning product goes into Stock or Supplier location with a incoming - shipment (depending on the settings of the supplier info in the - product form) -* You can make a delivery from the RMA to send a new product to the Customer - -.. warning:: Currently, the warranty duration used is the one configured on the - products today, not the one which was configured when the product - has been sold. - -Contributors: -------------- - - * Emmanuel Samyn - * Sébastien Beau - * Benoît Guillot - * Joel Grand-Guillaume - * Guewen Baconnier - * Yannick Vaucher - * Javier Carrasco - -""", - 'author': "Akretion, Camptocamp, Eezee-it, MONK Software, " + 'author': "Akretion, Camptocamp, Eezee-it, MONK Software, Vauxoo, " "Odoo Community Association (OCA)", 'website': 'http://www.akretion.com, http://www.camptocamp.com, ' - 'http://www.eezee-it.com, http://www.wearemonk.com', + 'http://www.eezee-it.com, http://www.wearemonk.com, ' + 'http://www.vauxoo.com', 'license': 'AGPL-3', 'depends': [ + 'purchase', 'sale', + 'sales_team', 'stock', - 'crm_claim', - 'crm_claim_code', - 'crm_claim_type', + 'crm_claim_rma_code', + 'crm_rma_location', 'product_warranty', ], 'data': [ - 'wizards/claim_make_picking.xml', - 'views/crm_claim_rma.xml', + 'data/ir_sequence_type.xml', + 'data/crm_case_section.xml', + 'data/crm_case_categ.xml', 'views/account_invoice.xml', + 'wizards/claim_make_picking.xml', + 'views/crm_claim.xml', + "views/claim_line.xml", 'views/res_partner.xml', - 'data/crm_claim_rma.xml', + 'views/stock_view.xml', 'security/ir.model.access.csv', ], - 'test': ['test/test_invoice_refund.yml'], - 'images': [ - 'images/product_return.png', - 'images/claim.png', - 'images/return_line.png', - 'images/exchange.png', + 'demo': [ + 'demo/account_invoice.xml', + 'demo/account_invoice_line.xml', + 'demo/crm_claim.xml', + 'demo/claim_line.xml', + ], + 'test': [ + 'test/test_invoice_refund.yml' ], 'installable': False, 'auto_install': False, diff --git a/crm_claim_rma/data/crm_claim_rma.xml b/crm_claim_rma/data/crm_case_categ.xml similarity index 58% rename from crm_claim_rma/data/crm_claim_rma.xml rename to crm_claim_rma/data/crm_case_categ.xml index e964d910..d9f88745 100644 --- a/crm_claim_rma/data/crm_claim_rma.xml +++ b/crm_claim_rma/data/crm_case_categ.xml @@ -1,103 +1,76 @@ - + - - - CRM Claim - crm.claim.rma - - - - CRM Claim - crm.claim.rma - - RMA-%(year)s/ - - - - - - After Sales Service - ASV - - - - - - + No Inventory - - - - - Customer Return - - - - - - Buyer Cancelled - - - - - - General Adjustement - - - - - - Could Not Ship - - - - - - Different Item - - - - - - Merchandise Not Received - - - - - - Merchandise Not As Described - - - - - - Pricing Error - - - - - - Shipping Address Undeliverable - - - - - - Delivered Late by Carrier - - + - + + Customer Return + + + + + + Buyer Cancelled + + + + + + General Adjustement + + + + + + Could Not Ship + + + + + + Different Item + + + + + + Merchandise Not Received + + + + + + Merchandise Not As Described + + + + + + Pricing Error + + + + + + Shipping Address Undeliverable + + + + + + Delivered Late by Carrier + + + + + Missed Fulfilment Promise - + diff --git a/crm_claim_rma/data/crm_case_section.xml b/crm_claim_rma/data/crm_case_section.xml new file mode 100644 index 00000000..fea22f4d --- /dev/null +++ b/crm_claim_rma/data/crm_case_section.xml @@ -0,0 +1,10 @@ + + + + + After Sales Service + ASV + + + + diff --git a/crm_claim_rma/data/ir_sequence_type.xml b/crm_claim_rma/data/ir_sequence_type.xml new file mode 100644 index 00000000..824e9b58 --- /dev/null +++ b/crm_claim_rma/data/ir_sequence_type.xml @@ -0,0 +1,16 @@ + + + + + CRM Claim + crm.claim.rma + + + + CRM Claim + crm.claim.rma + + RMA-%(year)s/ + + + diff --git a/crm_claim_rma/demo/account_invoice.xml b/crm_claim_rma/demo/account_invoice.xml new file mode 100644 index 00000000..70137484 --- /dev/null +++ b/crm_claim_rma/demo/account_invoice.xml @@ -0,0 +1,14 @@ + + + + + + + + draft + out_invoice + + + + + diff --git a/crm_claim_rma/demo/account_invoice_line.xml b/crm_claim_rma/demo/account_invoice_line.xml new file mode 100644 index 00000000..78e90ab6 --- /dev/null +++ b/crm_claim_rma/demo/account_invoice_line.xml @@ -0,0 +1,22 @@ + + + + + + + + iMac + 1799 + 3 + + + + + + + PC Assemble + 450 + 1 + + + diff --git a/crm_claim_rma/demo/claim_line.xml b/crm_claim_rma/demo/claim_line.xml new file mode 100644 index 00000000..01dffbdd --- /dev/null +++ b/crm_claim_rma/demo/claim_line.xml @@ -0,0 +1,28 @@ + + + + + Claim Line 1 of Claim 6 + + legal + + + 1 + 1799 + + 00013 + + + + Claim Line 2 of Claim 6 + + damaged + + + 1 + 450 + + 00012 + + + diff --git a/crm_claim_rma/demo/crm_claim.xml b/crm_claim_rma/demo/crm_claim.xml new file mode 100644 index 00000000..d10a4d83 --- /dev/null +++ b/crm_claim_rma/demo/crm_claim.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/crm_claim_rma/i18n/crm_claim_rma.pot b/crm_claim_rma/i18n/crm_claim_rma.pot index 5967dd30..b962f68f 100644 --- a/crm_claim_rma/i18n/crm_claim_rma.pot +++ b/crm_claim_rma/i18n/crm_claim_rma.pot @@ -75,7 +75,7 @@ msgid "Company" msgstr "" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Order cancellation" msgstr "" @@ -130,7 +130,7 @@ msgstr "" #. module: crm_claim_rma #: view:claim.line:0 -msgid "Compute Waranty" +msgid "Compute Warranty" msgstr "" #. module: crm_claim_rma @@ -211,7 +211,7 @@ msgid "To set the last state / substate change" msgstr "" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Not specified" msgstr "" @@ -252,7 +252,7 @@ msgid "Warehouse" msgstr "" #. module: crm_claim_rma -#: help:claim.line,claim_origine:0 +#: help:claim.line,claim_origin:0 msgid "To describe the line product problem" msgstr "" @@ -267,7 +267,7 @@ msgid "More" msgstr "" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Legal retractation" msgstr "" @@ -286,8 +286,8 @@ msgid "The warranty limit is computed as: invoice date + warranty defined on sel msgstr "" #. module: crm_claim_rma -#: field:claim.line,claim_origine:0 -#: model:ir.model.fields,field_description:crm_claim_rma.field_claim_line_claim_origine +#: field:claim.line,claim_origin:0 +#: model:ir.model.fields,field_description:crm_claim_rma.field_claim_line_claim_origin msgid "Claim Subject" msgstr "" @@ -366,12 +366,12 @@ msgid "Quotations and Sales" msgstr "" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Lost during transport" msgstr "" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Shipping error" msgstr "" @@ -563,13 +563,19 @@ msgid "Unit sale price of the product. Auto filled if retrun done by invoice sel msgstr "" #. module: crm_claim_rma -#: code:addons/crm_claim_rma/crm_claim_rma.py:313 +#: code:addons/crm_claim_rma/crm_claim_rma.py:473 #, python-format -msgid "Please set product and invoice." +msgid "Please set invoice first" msgstr "" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: code:addons/crm_claim_rma/crm_claim_rma.py:470 +#, python-format +msgid "Please set product first" +msgstr "" + +#. module: crm_claim_rma +#: selection:claim.line,claim_origin:0 msgid "Damaged delivered product" msgstr "" @@ -691,7 +697,7 @@ msgid "Merchandise Not As Described" msgstr "" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Exchange request" msgstr "" @@ -717,7 +723,7 @@ msgid "Current" msgstr "" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 #: selection:crm.claim,claim_type:0 msgid "Other" msgstr "" @@ -889,3 +895,17 @@ msgstr "" msgid "The product has no supplier configured." msgstr "" +#. module: crm_claim_rma +#: help:claim.line,number:0 +msgid "Claim Line Identification Number" +msgstr "Número de idetifiación de la línea de reclamo" + +#. module: crm_claim_rma +#: field:crm.claim,rma_number:0 +msgid "RMA Number" +msgstr "Número RMA" + +#. module: crm_claim_rma +#: help:crm.claim,rma_number:0 +msgid "RMA Number provided by supplier" +msgstr "Número RMA asignado por el proveedor" diff --git a/crm_claim_rma/i18n/es.po b/crm_claim_rma/i18n/es.po index 80a235b9..6cad68f8 100644 --- a/crm_claim_rma/i18n/es.po +++ b/crm_claim_rma/i18n/es.po @@ -1,20 +1,19 @@ # Translation of OpenERP Server. # This file contains the translation of the following modules: -# * crm_claim_rma +# * crm_claim_rma # msgid "" msgstr "" -"Project-Id-Version: OpenERP Server 6.1\n" +"Project-Id-Version: OpenERP Server 7.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-12-20 18:21+0000\n" -"PO-Revision-Date: 2014-01-22 19:38+0000\n" -"Last-Translator: Pedro Manuel Baeza \n" +"PO-Revision-Date: 2013-12-20 18:21+0000\n" +"Last-Translator: <>\n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-05-24 06:50+0000\n" -"X-Generator: Launchpad (build 17017)\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" #. module: crm_claim_rma #: view:claim.line:0 @@ -73,10 +72,10 @@ msgstr "Linea de Abono" #. module: crm_claim_rma #: selection:claim.line,applicable_guarantee:0 msgid "Company" -msgstr "Compañia" +msgstr "Compañía" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Order cancellation" msgstr "Cancelación de orden" @@ -84,7 +83,7 @@ msgstr "Cancelación de orden" #: field:claim_make_picking.wizard,claim_line_dest_location:0 #: model:ir.model.fields,field_description:crm_claim_rma.field_claim_make_picking_wizard_claim_line_dest_location msgid "Dest. Location" -msgstr "Localizalización de destino" +msgstr "Localización de destino" #. module: crm_claim_rma #: code:addons/crm_claim_rma/crm_claim_rma.py:224 @@ -132,7 +131,7 @@ msgstr "¡Ya se ha creado un abono para esta reclamación!" #. module: crm_claim_rma #: view:claim.line:0 -msgid "Compute Waranty" +msgid "Compute Warranty" msgstr "Calcular garantía" #. module: crm_claim_rma @@ -183,7 +182,7 @@ msgstr "Devolución de cliente" #. module: crm_claim_rma #: model:ir.model,name:crm_claim_rma.model_stock_move msgid "Stock Move" -msgstr "Movimientos de stock" +msgstr "Movimientos de Inventario" #. module: crm_claim_rma #: help:claim.line,product_returned_quantity:0 @@ -228,7 +227,7 @@ msgid "To set the last state / substate change" msgstr "Para definir el último cambio de estado / sub-estado" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Not specified" msgstr "No especificado" @@ -269,7 +268,7 @@ msgid "Warehouse" msgstr "Almacén" #. module: crm_claim_rma -#: help:claim.line,claim_origine:0 +#: help:claim.line,claim_origin:0 msgid "To describe the line product problem" msgstr "Para describir el problema de la línea de producto" @@ -285,7 +284,7 @@ msgid "More" msgstr "Más" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Legal retractation" msgstr "Retractación legal" @@ -308,10 +307,10 @@ msgstr "" "seleccionada en el producto seleccionado." #. module: crm_claim_rma -#: field:claim.line,claim_origine:0 -#: model:ir.model.fields,field_description:crm_claim_rma.field_claim_line_claim_origine +#: field:claim.line,claim_origin:0 +#: model:ir.model.fields,field_description:crm_claim_rma.field_claim_line_claim_origin msgid "Claim Subject" -msgstr "Objeto de la reclamación" +msgstr "Motivo de la reclamación" #. module: crm_claim_rma #: field:crm.claim,real_cost:0 @@ -388,12 +387,12 @@ msgid "Quotations and Sales" msgstr "Presupuestos y pedidos" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Lost during transport" msgstr "Perdido durante el transporte" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Shipping error" msgstr "Error de envío" @@ -455,7 +454,7 @@ msgstr "Documentos generados" #. module: crm_claim_rma #: help:claim.line,prodlot_id:0 msgid "The serial/lot of the returned product" -msgstr "Nº de serie/lote del producto devuelto" +msgstr "No de Serie/Lote del producto devuelto" #. module: crm_claim_rma #: view:crm.claim:0 @@ -583,8 +582,8 @@ msgstr "Fabricante" #. module: crm_claim_rma #: field:claim.line,prodlot_id:0 #: model:ir.model.fields,field_description:crm_claim_rma.field_claim_line_prodlot_id -msgid "Serial/Lot n°" -msgstr "N° de serie/lote:" +msgid "Serial/Lot number" +msgstr "No. de Serial/Lote" #. module: crm_claim_rma #: help:claim.line,unit_sale_price:0 @@ -600,13 +599,19 @@ msgstr "" "descuento de facturas, puede ser 0 si el producto es gratuito, etc..." #. module: crm_claim_rma -#: code:addons/crm_claim_rma/crm_claim_rma.py:313 +#: code:addons/crm_claim_rma/crm_claim_rma.py:473 #, python-format -msgid "Please set product and invoice." -msgstr "Establezca por favor producto y factura." +msgid "Please set invoice first" +msgstr "Por favor, establezca la factura primero" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: code:addons/crm_claim_rma/crm_claim_rma.py:470 +#, python-format +msgid "Please set product first" +msgstr "Por favor, establezca el producto primero" + +#. module: crm_claim_rma +#: selection:claim.line,claim_origin:0 msgid "Damaged delivered product" msgstr "Producto enviado dañado" @@ -728,7 +733,7 @@ msgid "Merchandise Not As Described" msgstr "El producto no se corresponde con la descripción" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Exchange request" msgstr "Solicitar cambio" @@ -754,7 +759,7 @@ msgid "Current" msgstr "Actual" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 #: selection:crm.claim,claim_type:0 msgid "Other" msgstr "Otro" @@ -932,3 +937,132 @@ msgstr "Reclamaciones en proceso" #, python-format msgid "The product has no supplier configured." msgstr "El producto no tiene proveedor configurado." + +#. module: crm_claim_rma +#: view:crm.claim:0 +#: field:crm.claim,pick:0 +msgid "Pick the product in store" +msgstr "Buscar el producto en la tienda" + +#. module: crm_claim_rma +#: view:crm.claim:0 +#: field:crm.claim,delivery_address_id:0 +msgid "Partner delivery address" +msgstr "Dirección de envío" + +#. module: crm_claim_rma +#: view:crm.claim:crm_claim_rma.crm_claim_rma_form_view +#: model:ir.actions.act_window,name:crm_claim_rma.act_crm_claim_rma_purchase_orders +msgid "Purchases" +msgstr "Compras" + +#. module: crm_claim_rma +#: view:crm.claim:crm_claim_rma.crm_claim_rma_form_view +#: model:ir.actions.act_window,name:crm_claim_rma.act_crm_claim_rma_invoice +msgid "Invoices" +msgstr "Facturas" + +#. module: crm_claim_rma +#: view:crm.claim:crm_claim_rma.crm_claim_rma_form_view +#: model:ir.actions.act_window,name:crm_claim_rma.act_crm_claim_rma_picking_in +msgid "Returns" +msgstr "Devoluciones" + +#. module: crm_claim_rma +#: view:crm.claim:crm_claim_rma.crm_claim_rma_form_view +#: model:ir.actions.act_window,name:crm_claim_rma.act_crm_claim_rma_sale_orders +msgid "Sales" +msgstr "Ventas" + +#. module: crm_claim_rma +#: view:claim.line:crm_claim_rma.view_crm_claim_lines_filter +#: field:claim.line,priority:0 +msgid "Priority" +msgstr "Prioridad" + +#. module: crm_claim_rma +#: view:crm.claim:crm_claim_rma.crm_claim_rma_form_view +msgid "Product Returns" +msgstr "Productos Devueltos" + +#. module: crm_claim_rma +#: view:crm.claim:0 +#: field:crm.claim,pick:0 +msgid "Pick the product in the store" +msgstr "Retirar el producto en recepción" + +#. module: crm_claim_rma +#: field:claim.line,number:0 +msgid "Number" +msgstr "Número" + +#. module: crm_claim_rma +#: selection:claim.line,warning:0 +msgid "Valid" +msgstr "Válida" + +#. module: crm_claim_rma +#: selection:claim.line,warning:0 +msgid "Expired" +msgstr "Expirada" + +#. module: crm_claim_rma +#: selection:claim.line,warning:0 +msgid "Not Defined" +msgstr "No Definida" + +#. module: crm_claim_rma +#: selection:claim.line,warranty_type:0 +msgid "Company" +msgstr "Compañía" + +#. module: crm_claim_rma +#: selection:claim.line,warranty_type:0 +msgid "Supplier" +msgstr "Proveedor" + +#. module: crm_claim_rma +#: selection:claim.line,warranty_type:0 +msgid "Other" +msgstr "Otro" + +#. module: crm_claim_rma +#: field:crm.claim,pick:0 +msgid "Pick the product in the store" +msgstr "Retirar el producto en recepción" + +#. module: crm_claim_rma +#: field:crm.claim,name:0 +msgid "Name" +msgstr "Reclamo" + +#. module: crm_claim_rma +#: field:crm.claim,rma_number:0 +msgid "Rma number" +msgstr "Número RMA" + +#. module: crm_claim_rma +#: selection:claim.line,claim_origin:0 +msgid "Imperfection" +msgstr "Imperfección" + +#. module: crm_claim_rma +#: selection:claim.line,claim_origin:0 +msgid "Physical Damage by Client" +msgstr "Daño físico por el cliente" + +#. module: crm_claim_rma +#: selection:claim.line,claim_origin:0 +msgid "Physical Damage by Company" +msgstr "Daño físico por la compañía" + +#. module: crm_claim_rma +#: selection:claim.line,claim_origin:0 +msgid "Perfect Conditions" +msgstr "Perfectas condiciones" + +#. module: yoytec_customer_rma_workflow +#: field:claim.line,claim_diagnosis:0 +msgid "Claim diagnosis" +msgstr "Diagnóstico del reclamo" + diff --git a/crm_claim_rma/i18n/es_MX.po b/crm_claim_rma/i18n/es_MX.po new file mode 100644 index 00000000..80297083 --- /dev/null +++ b/crm_claim_rma/i18n/es_MX.po @@ -0,0 +1,16 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * crm_claim_rma +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-07-17 18:30+0000\n" +"PO-Revision-Date: 2015-07-17 18:30+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" diff --git a/crm_claim_rma/i18n/es_PA.po b/crm_claim_rma/i18n/es_PA.po new file mode 100644 index 00000000..80297083 --- /dev/null +++ b/crm_claim_rma/i18n/es_PA.po @@ -0,0 +1,16 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * crm_claim_rma +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-07-17 18:30+0000\n" +"PO-Revision-Date: 2015-07-17 18:30+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" diff --git a/crm_claim_rma/i18n/es_VE.po b/crm_claim_rma/i18n/es_VE.po new file mode 100644 index 00000000..80297083 --- /dev/null +++ b/crm_claim_rma/i18n/es_VE.po @@ -0,0 +1,16 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * crm_claim_rma +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-07-17 18:30+0000\n" +"PO-Revision-Date: 2015-07-17 18:30+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" diff --git a/crm_claim_rma/i18n/fr.po b/crm_claim_rma/i18n/fr.po index bb5a1c8f..36a1f4fa 100644 --- a/crm_claim_rma/i18n/fr.po +++ b/crm_claim_rma/i18n/fr.po @@ -77,7 +77,7 @@ msgid "Company" msgstr "Société" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Order cancellation" msgstr "Annulation de la commande" @@ -132,7 +132,7 @@ msgstr "Un avoir a déjà été créé pour cette réclamation !" #. module: crm_claim_rma #: view:claim.line:0 -msgid "Compute Waranty" +msgid "Compute Warranty" msgstr "" #. module: crm_claim_rma @@ -220,7 +220,7 @@ msgid "To set the last state / substate change" msgstr "Pour définir le derniere changement d'état ou de sous-état" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Not specified" msgstr "Non spécifié" @@ -261,7 +261,7 @@ msgid "Warehouse" msgstr "Entrepôt" #. module: crm_claim_rma -#: help:claim.line,claim_origine:0 +#: help:claim.line,claim_origin:0 msgid "To describe the line product problem" msgstr "Pour décrire le problème de la ligne de produit" @@ -277,7 +277,7 @@ msgid "More" msgstr "Plus" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Legal retractation" msgstr "Rétracation légale" @@ -300,8 +300,8 @@ msgstr "" "dans le produit sélectionné." #. module: crm_claim_rma -#: field:claim.line,claim_origine:0 -#: model:ir.model.fields,field_description:crm_claim_rma.field_claim_line_claim_origine +#: field:claim.line,claim_origin:0 +#: model:ir.model.fields,field_description:crm_claim_rma.field_claim_line_claim_origin msgid "Claim Subject" msgstr "Sujet de la réclamation" @@ -380,12 +380,12 @@ msgid "Quotations and Sales" msgstr "" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Lost during transport" msgstr "Perte pendant le transport" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Shipping error" msgstr "Erreur d'expédition" @@ -593,13 +593,19 @@ msgstr "" "sur la facture,..." #. module: crm_claim_rma -#: code:addons/crm_claim_rma/crm_claim_rma.py:313 +#: code:addons/crm_claim_rma/crm_claim_rma.py:473 #, python-format -msgid "Please set product and invoice." -msgstr "" +msgid "Please set invoice first" +msgstr "S'il vous plaît d'abord définir le facture" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: code:addons/crm_claim_rma/crm_claim_rma.py:470 +#, python-format +msgid "Please set product first" +msgstr "S'il vous plaît d'abord définir le produit" + +#. module: crm_claim_rma +#: selection:claim.line,claim_origin:0 msgid "Damaged delivered product" msgstr "Produit livré endommagé" @@ -721,7 +727,7 @@ msgid "Merchandise Not As Described" msgstr "Produit ne correspondant pas à sa description" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Exchange request" msgstr "Demande d'échange" @@ -747,7 +753,7 @@ msgid "Current" msgstr "Actuel" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 #: selection:crm.claim,claim_type:0 msgid "Other" msgstr "Autre" diff --git a/crm_claim_rma/i18n/pt_BR.po b/crm_claim_rma/i18n/pt_BR.po index 98791d33..f63f7440 100644 --- a/crm_claim_rma/i18n/pt_BR.po +++ b/crm_claim_rma/i18n/pt_BR.po @@ -77,7 +77,7 @@ msgid "Company" msgstr "Empresa" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Order cancellation" msgstr "" @@ -132,7 +132,7 @@ msgstr "" #. module: crm_claim_rma #: view:claim.line:0 -msgid "Compute Waranty" +msgid "Compute Warranty" msgstr "" #. module: crm_claim_rma @@ -220,7 +220,7 @@ msgid "To set the last state / substate change" msgstr "" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Not specified" msgstr "" @@ -261,7 +261,7 @@ msgid "Warehouse" msgstr "" #. module: crm_claim_rma -#: help:claim.line,claim_origine:0 +#: help:claim.line,claim_origin:0 msgid "To describe the line product problem" msgstr "" @@ -276,7 +276,7 @@ msgid "More" msgstr "" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Legal retractation" msgstr "" @@ -297,8 +297,8 @@ msgid "" msgstr "" #. module: crm_claim_rma -#: field:claim.line,claim_origine:0 -#: model:ir.model.fields,field_description:crm_claim_rma.field_claim_line_claim_origine +#: field:claim.line,claim_origin:0 +#: model:ir.model.fields,field_description:crm_claim_rma.field_claim_line_claim_origin msgid "Claim Subject" msgstr "Assunto da Solicitação" @@ -377,12 +377,12 @@ msgid "Quotations and Sales" msgstr "" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Lost during transport" msgstr "" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Shipping error" msgstr "" @@ -582,13 +582,7 @@ msgid "" msgstr "" #. module: crm_claim_rma -#: code:addons/crm_claim_rma/crm_claim_rma.py:313 -#, python-format -msgid "Please set product and invoice." -msgstr "" - -#. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Damaged delivered product" msgstr "" @@ -710,7 +704,7 @@ msgid "Merchandise Not As Described" msgstr "" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 msgid "Exchange request" msgstr "" @@ -736,7 +730,7 @@ msgid "Current" msgstr "" #. module: crm_claim_rma -#: selection:claim.line,claim_origine:0 +#: selection:claim.line,claim_origin:0 #: selection:crm.claim,claim_type:0 msgid "Other" msgstr "" diff --git a/crm_claim_rma/models/__init__.py b/crm_claim_rma/models/__init__.py index e65150a8..bd35d8bb 100644 --- a/crm_claim_rma/models/__init__.py +++ b/crm_claim_rma/models/__init__.py @@ -1,3 +1,33 @@ -from . import crm_claim_rma +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright 2015 Vauxoo +# Copyright 2013 Camptocamp +# Copyright 2009-2013 Akretion, +# Author: Emmanuel Samyn, Raphaël Valyi, Sébastien Beau, +# Benoît Guillot, Joel Grand-Guillaume, +# Osval Reyes +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## from . import account_invoice -from . import stock +from . import account_invoice_line +from . import claim_line +from . import crm_claim +from . import invoice_no_date +from . import product_no_supplier +from . import stock_move +from . import stock_picking +from . import substate_substate diff --git a/crm_claim_rma/models/account_invoice.py b/crm_claim_rma/models/account_invoice.py index 012a0e61..116591a1 100644 --- a/crm_claim_rma/models/account_invoice.py +++ b/crm_claim_rma/models/account_invoice.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- ############################################################################## # -# Copyright 2015 Eezee-It, MONK Software +# Copyright 2015 Eezee-It, MONK Software, Vauxoo # Copyright 2013 Camptocamp # Copyright 2009-2013 Akretion, # Author: Emmanuel Samyn, Raphaël Valyi, Sébastien Beau, # Benoît Guillot, Joel Grand-Guillaume, Leonardo Donelli +# Osval Reyes, Yanina Aular # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -22,21 +23,21 @@ # ############################################################################## -from openerp import models, fields, api, exceptions -from openerp.tools.translate import _ +from openerp import _, api, exceptions, fields, models class AccountInvoice(models.Model): + _inherit = "account.invoice" claim_id = fields.Many2one('crm.claim', string='Claim') - @api.model def _refund_cleanup_lines(self, lines): """ Override when from claim to update the quantity and link to the claim line. """ + # check if is an invoice_line and we are from a claim if not (self.env.context.get('claim_line_ids') and lines and lines[0]._name == 'account.invoice.line'): @@ -59,14 +60,17 @@ class AccountInvoice(models.Model): elif column_type not in ('many2many', 'one2many'): clean_line[field_name] = inv_line[field_name] elif field_name == 'invoice_line_tax_id': + tax_ids = inv_line[field_name].ids clean_line[field_name] = [(6, 0, tax_ids)] clean_line['quantity'] = claim_line.product_returned_quantity clean_line['claim_line_id'] = [claim_line.id] + new_lines.append(clean_line) if not new_lines: # TODO use custom states to show button of this wizard or # not instead of raise an error + raise exceptions.Warning( _('A refund has already been created for this claim !')) return [(0, 0, l) for l in new_lines] @@ -82,21 +86,3 @@ class AccountInvoice(models.Model): result['claim_id'] = self.env.context['claim_id'] return result - - -class AccountInvoiceLine(models.Model): - _inherit = "account.invoice.line" - - @api.model - @api.returns('self', lambda value: value.id) - def create(self, vals): - claim_line_id = vals.get('claim_line_id') - if claim_line_id: - del vals['claim_line_id'] - - line = super(AccountInvoiceLine, self).create(vals) - if claim_line_id: - claim_line = self.env['claim.line'].browse(claim_line_id) - claim_line.refund_line_id = line.id - - return line diff --git a/crm_claim_rma/models/account_invoice_line.py b/crm_claim_rma/models/account_invoice_line.py new file mode 100644 index 00000000..eeb8aa9d --- /dev/null +++ b/crm_claim_rma/models/account_invoice_line.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright 2015 Vauxoo +# Copyright 2013 Camptocamp +# Copyright 2009-2013 Akretion, +# Author: Emmanuel Samyn, Raphaël Valyi, Sébastien Beau, +# Benoît Guillot, Joel Grand-Guillaume, +# Osval Reyes, Yanina Aular +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp import api, models + + +class AccountInvoiceLine(models.Model): + + _inherit = "account.invoice.line" + + @api.model + def create(self, vals): + claim_line_id = vals.get('claim_line_id') + if claim_line_id: + del vals['claim_line_id'] + + line = super(AccountInvoiceLine, self).create(vals) + if claim_line_id: + claim_line = self.env['claim.line'].browse(claim_line_id) + claim_line.refund_line_id = line.id + + return line diff --git a/crm_claim_rma/models/claim_line.py b/crm_claim_rma/models/claim_line.py new file mode 100644 index 00000000..f91f692d --- /dev/null +++ b/crm_claim_rma/models/claim_line.py @@ -0,0 +1,432 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright 2015 Vauxoo +# Copyright 2013 Camptocamp +# Copyright 2009-2013 Akretion, +# Author: Emmanuel Samyn, Raphaël Valyi, Sébastien Beau, +# Benoît Guillot, Joel Grand-Guillaume, +# Osval Reyes, Yanina Aular +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +import calendar +import math +from datetime import datetime + +from dateutil.relativedelta import relativedelta + +from openerp import _, api, exceptions, fields, models +from openerp.tools import (DEFAULT_SERVER_DATE_FORMAT, + DEFAULT_SERVER_DATETIME_FORMAT) + +from .invoice_no_date import InvoiceNoDate +from .product_no_supplier import ProductNoSupplier + + +class ClaimLine(models.Model): + + _name = "claim.line" + + _inherit = 'mail.thread' + _description = "List of product to return" + _rec_name = "display_name" + + SUBJECT_LIST = [('none', 'Not specified'), + ('legal', 'Legal retractation'), + ('cancellation', 'Order cancellation'), + ('damaged', 'Damaged delivered product'), + ('error', 'Shipping error'), + ('exchange', 'Exchange request'), + ('lost', 'Lost during transport'), + ('perfect_conditions', + 'Perfect Conditions'), + ('imperfection', 'Imperfection'), + ('physical_damage_client', + 'Physical Damage by Client'), + ('physical_damage_company', + 'Physical Damage by Company'), + ('other', 'Other')] + WARRANT_COMMENT = [ + ('valid', _("Valid")), + ('expired', _("Expired")), + ('not_define', _("Not Defined"))] + + number = fields.Char( + readonly=True, + default='/', + help='Claim Line Identification Number') + company_id = fields.Many2one( + 'res.company', string='Company', readonly=False, + change_default=True, + default=lambda self: self.env['res.company']._company_default_get( + 'claim.line')) + date = fields.Date('Claim Line Date', + select=True, + default=fields.date.today()) + name = fields.Char('Description', default='none', required=True, + help="More precise description of the problem") + priority = fields.Selection([('0_not_define', 'Not Define'), + ('1_normal', 'Normal'), + ('2_high', 'High'), + ('3_very_high', 'Very High')], + 'Priority', default='0_not_define', + compute='_set_priority', + store=True, + readonly=False, + help="Priority attention of claim line") + claim_diagnosis = fields.\ + Selection([('damaged', 'Product Damaged'), + ('repaired', 'Product Repaired'), + ('good', 'Product in good condition'), + ('hidden', 'Product with hidden physical damage'), + ], + help="To describe the line product diagnosis") + claim_origin = fields.Selection(SUBJECT_LIST, 'Claim Subject', + required=True, help="To describe the " + "line product problem") + product_id = fields.Many2one('product.product', string='Product', + help="Returned product") + product_returned_quantity = \ + fields.Float('Quantity', digits=(12, 2), + help="Quantity of product returned") + unit_sale_price = fields.Float(digits=(12, 2), + help="Unit sale price of the product. " + "Auto filled if retrun done " + "by invoice selection. Be careful " + "and check the automatic " + "value as don't take into account " + "previous refunds, invoice " + "discount, can be for 0 if product " + "for free,...") + return_value = fields.Float(compute='_compute_line_total_amount', + string='Total return', + help="Quantity returned * Unit sold price",) + prodlot_id = fields.Many2one('stock.production.lot', + string='Serial/Lot number', + help="The serial/lot of " + "the returned product") + applicable_guarantee = fields.Selection([('us', 'Company'), + ('supplier', 'Supplier'), + ('brand', 'Brand manufacturer')], + 'Warranty type') + guarantee_limit = fields.Date('Warranty limit', readonly=True, + help="The warranty limit is " + "computed as: invoice date + warranty " + "defined on selected product.") + warning = fields.Selection(WARRANT_COMMENT, + 'Warranty', readonly=True, + help="If warranty has expired") + display_name = fields.Char('Name', compute='_get_display_name') + + @api.model + def get_warranty_return_partner(self): + return self.env['product.supplierinfo'].get_warranty_return_partner() + + warranty_type = fields.Selection( + get_warranty_return_partner, readonly=True, + help="Who is in charge of the warranty return treatment towards " + "the end customer. Company will use the current company " + "delivery or default address and so on for supplier and brand " + "manufacturer. Does not necessarily mean that the warranty " + "to be applied is the one of the return partner (ie: can be " + "returned to the company and be under the brand warranty") + warranty_return_partner = \ + fields.Many2one('res.partner', string='Warranty Address', + help="Where the customer has to " + "send back the product(s)") + claim_id = fields.Many2one('crm.claim', string='Related claim', + ondelete='cascade', + help="To link to the case.claim object") + state = fields.Selection([('draft', 'Draft'), ('refused', 'Refused'), + ('confirmed', 'Confirmed, waiting for product'), + ('in_to_control', 'Received, to control'), + ('in_to_treate', 'Controlled, to treate'), + ('treated', 'Treated')], + string='State', default='draft') + substate_id = fields.Many2one('substate.substate', string='Sub state', + help="Select a sub state to precise the " + "standard state. Example 1: " + "state = refused; substate could " + "be warranty over, not in " + "warranty, no problem,... . " + "Example 2: state = to treate; " + "substate could be to refund, to " + "exchange, to repair,...") + last_state_change = fields.Date(string='Last change', help="To set the" + "last state / substate change") + invoice_line_id = fields.Many2one('account.invoice.line', + string='Invoice Line', + help='The invoice line related' + ' to the returned product') + refund_line_id = fields.Many2one('account.invoice.line', + string='Refund Line', + help='The refund line related' + ' to the returned product') + move_in_id = fields.Many2one('stock.move', + string='Move Line from picking in', + help='The move line related' + ' to the returned product') + move_out_id = fields.Many2one('stock.move', + string='Move Line from picking out', + help='The move line related' + ' to the returned product') + location_dest_id = fields.Many2one('stock.location', + string='Return Stock Location', + help='The return stock location' + ' of the returned product') + claim_type = fields.Many2one(related='claim_id.claim_type', + string="Claim Line Type", + store=True, help="Claim classification") + invoice_date = fields.Datetime(related='invoice_line_id.invoice_id.' + 'create_date', + help="Date of Claim Invoice") + + # Method to calculate total amount of the line : qty*UP + @api.multi + def _compute_line_total_amount(self): + for line in self: + line.return_value = (line.unit_sale_price * + line.product_returned_quantity) + + @api.multi + def copy(self, default=None): + self.ensure_one() + default = default or {} + std_default = { + 'move_in_id': False, + 'move_out_id': False, + 'refund_line_id': False, + } + std_default.update(default) + return super(ClaimLine, self).copy(default=std_default) + + @api.depends('invoice_date', 'date') + def _set_priority(self): + """ + To determine the priority of claim line + """ + for line_id in self: + if line_id.invoice_date: + days = fields.datetime.strptime(line_id.date, '%Y-%m-%d') - \ + fields.datetime.strptime(line_id.invoice_date, + DEFAULT_SERVER_DATETIME_FORMAT) + if days.days <= 1: + line_id.priority = '3_very_high' + elif days.days <= 7: + line_id.priority = '2_high' + else: + line_id.priority = '1_normal' + + def _get_subject(self, num): + if num > 0 and num <= len(self.SUBJECT_LIST): + return self.SUBJECT_LIST[num - 1][0] + else: + return self.SUBJECT_LIST[0][0] + + @staticmethod + def warranty_limit(start, warranty_duration): + """ Take a duration in float, return the duration in relativedelta + + ``relative_delta(months=...)`` only accepts integers. + We have to extract the decimal part, and then, extend the delta with + days. + + """ + decimal_part, months = math.modf(warranty_duration) + months = int(months) + # If we have a decimal part, we add the number them as days to + # the limit. We need to get the month to know the number of + # days. + delta = relativedelta(months=months) + monthday = start + delta + __, days_month = calendar.monthrange(monthday.year, monthday.month) + # ignore the rest of the days (hours) since we expect a date + days = int(days_month * decimal_part) + return start + relativedelta(months=months, days=days) + + def _warranty_limit_values(self, invoice, claim_type, product, claim_date): + if not (invoice and claim_type and product and claim_date): + return {'guarantee_limit': False, 'warning': False} + + invoice_date = invoice.create_date + if not invoice_date: + raise InvoiceNoDate + + warning = 'not_define' + invoice_date = datetime.strptime(invoice_date, + DEFAULT_SERVER_DATETIME_FORMAT) + + if isinstance(claim_type, self.env['crm.claim.type'].__class__): + claim_type = claim_type.id + + if claim_type == self.env.ref('crm_claim_type.' + 'crm_claim_type_supplier').id: + try: + warranty_duration = product.seller_ids[0].warranty_duration + except IndexError: + raise ProductNoSupplier + else: + warranty_duration = product.warranty + + limit = self.warranty_limit(invoice_date, warranty_duration) + if warranty_duration > 0: + claim_date = datetime.strptime(claim_date, + DEFAULT_SERVER_DATETIME_FORMAT) + if limit < claim_date: + warning = 'expired' + else: + warning = 'valid' + + return {'guarantee_limit': limit.strftime(DEFAULT_SERVER_DATE_FORMAT), + 'warning': warning} + + def set_warranty_limit(self): + self.ensure_one() + + claim = self.claim_id + invoice_id = self.invoice_line_id and self.invoice_line_id.invoice_id \ + or claim.invoice_id + try: + values = self._warranty_limit_values( + invoice_id, claim.claim_type, + self.product_id, claim.date) + except InvoiceNoDate: + raise exceptions.Warning( + _('Error'), _('Cannot find any date for invoice. ' + 'Must be a validated invoice.')) + except ProductNoSupplier: + raise exceptions.Warning( + _('Error'), _('The product has no supplier configured.')) + + self.write(values) + return True + + @api.model + def auto_set_warranty(self): + """ Set warranty automatically + if the user has not himself pressed on 'Calculate warranty state' + button, it sets warranty for him""" + for line in self: + if not line.warning: + line.set_warranty() + return True + + @api.returns('stock.location') + def get_destination_location(self, product_id, warehouse_id): + """ + Compute and return the destination location to take + for a return. Always take 'Supplier' one when return type different + from company. + """ + location_dest_id = warehouse_id.lot_stock_id + + if product_id.seller_ids: + seller = product_id.seller_ids[0] + if seller.warranty_return_partner != 'company' \ + and seller.name and \ + seller.name.property_stock_supplier: + location_dest_id = seller.name.property_stock_supplier + + return location_dest_id + + def _warranty_return_address_values(self, product, company, warehouse): + """ + Return the partner to be used as return destination and + the destination stock location of the line in case of return. + + We can have various cases here: + - company or other: return to company partner or + crm_return_address_id if specified + - supplier: return to the supplier address + """ + if not (product and company and warehouse): + return { + 'warranty_return_partner': False, + 'warranty_type': False, + 'location_dest_id': False + } + sellers = product.seller_ids + if sellers: + seller = sellers[0] + return_address_id = seller.warranty_return_address.id + return_type = seller.warranty_return_partner + else: + # when no supplier is configured, returns to the company + return_address = (company.crm_return_address_id or + company.partner_id) + return_address_id = return_address.id + return_type = 'company' + location_dest = self.get_destination_location(product, warehouse) + return { + 'warranty_return_partner': return_address_id, + 'warranty_type': return_type, + 'location_dest_id': location_dest.id + } + + def set_warranty_return_address(self): + self.ensure_one() + claim = self.claim_id + values = self._warranty_return_address_values( + self.product_id, claim.company_id, claim.warehouse_id) + self.write(values) + return True + + @api.multi + def set_warranty(self): + """ + Calculate warranty limit and address + """ + for line_id in self: + if not line_id.product_id: + raise exceptions.Warning( + _('Error'), _('Please set product first')) + + if not line_id.invoice_line_id: + raise exceptions.Warning( + _('Error'), _('Please set invoice first')) + + line_id.set_warranty_limit() + line_id.set_warranty_return_address() + + @api.model + def _get_sequence_number(self): + """ + @return the value of the sequence for the number field in the + claim.line model. + """ + return self.env['ir.sequence'].get('claim.line') + + @api.model + def create(self, vals): + """ + @return write the identify number once the claim line is create. + """ + vals = vals or {} + + if ('number' not in vals) or (vals.get('number', False) == '/'): + vals['number'] = self._get_sequence_number() + + res = super(ClaimLine, self).create(vals) + return res + + @api.multi + def _get_display_name(self): + res = [] + for line_id in self: + res.append( + (line_id.id, "%s - %s" % + (line_id.claim_id.code, line_id.name))) + return res diff --git a/crm_claim_rma/models/crm_claim.py b/crm_claim_rma/models/crm_claim.py new file mode 100644 index 00000000..ec8824fd --- /dev/null +++ b/crm_claim_rma/models/crm_claim.py @@ -0,0 +1,198 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright 2015 Eezee-It, MONK Software, Vauxoo +# Copyright 2013 Camptocamp +# Copyright 2009-2013 Akretion, +# Author: Emmanuel Samyn, Raphaël Valyi, Sébastien Beau, +# Benoît Guillot, Joel Grand-Guillaume, Leonardo Donelli, +# Osval Reyes, Yanina Aular +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp import _, api, exceptions, fields, models + +from .invoice_no_date import InvoiceNoDate +from .product_no_supplier import ProductNoSupplier + + +class CrmClaim(models.Model): + _inherit = 'crm.claim' + + def _get_default_warehouse(self): + company_id = self.env.user.company_id.id + wh_obj = self.env['stock.warehouse'] + wh = wh_obj.search([('company_id', '=', company_id)], limit=1) + if not wh: + raise exceptions.Warning( + _('There is no warehouse for the current user\'s company.')) + return wh + + @api.multi + def name_get(self): + res = [] + for claim in self: + code = claim.code and str(claim.code) or '' + res.append((claim.id, '[' + code + '] ' + claim.name)) + return res + + company_id = fields.Many2one(change_default=True, + default=lambda self: + self.env['res.company']._company_default_get( + 'crm.claim')) + + claim_line_ids = fields.One2many('claim.line', 'claim_id', + string='Return lines') + planned_revenue = fields.Float('Expected revenue') + planned_cost = fields.Float('Expected cost') + real_revenue = fields.Float() + real_cost = fields.Float() + invoice_ids = fields.One2many('account.invoice', 'claim_id', 'Refunds', + copy=False) + picking_ids = fields.One2many('stock.picking', 'claim_id', 'RMA', + copy=False) + invoice_id = fields.Many2one('account.invoice', string='Invoice', + help='Related original Cusotmer invoice') + pick = fields.Boolean('Pick the product in the store') + delivery_address_id = fields.Many2one('res.partner', + string='Partner delivery address', + help="This address will be used to " + "deliver repaired or replacement " + "products.") + sequence = fields.Integer(default=lambda *args: 1) + warehouse_id = fields.Many2one('stock.warehouse', string='Warehouse', + required=True, + default=_get_default_warehouse) + rma_number = fields.Char(size=128, help='RMA Number provided by supplier') + + @api.model + def _get_claim_type_default(self): + claim_type = self.env['crm.claim.type'].search([]) + return claim_type[0] if claim_type else self.env['crm.claim.type'] + + claim_type = \ + fields.Many2one(default=_get_claim_type_default, + help="Claim classification", + required=True) + + @api.onchange('invoice_id', 'warehouse_id', 'claim_type', 'date') + def _onchange_invoice_warehouse_type_date(self): + context = self.env.context + claim_line = self.env['claim.line'] + invoice_lines = self.invoice_id.invoice_line + if not self.warehouse_id: + self.warehouse_id = self._get_default_warehouse() + claim_type = self.claim_type + claim_date = self.date + warehouse = self.warehouse_id + company = self.company_id + create_lines = context.get('create_lines') + + def warranty_values(invoice, product): + values = {} + try: + warranty = claim_line._warranty_limit_values( + invoice, claim_type, product, claim_date) + except (InvoiceNoDate, ProductNoSupplier): + # we don't mind at this point if the warranty can't be + # computed and we don't want to block the user + values.update({'guarantee_limit': False, 'warning': False}) + else: + values.update(warranty) + + warranty_address = claim_line._warranty_return_address_values( + product, company, warehouse) + values.update(warranty_address) + return values + + if create_lines: # happens when the invoice is changed + claim_lines = [] + for invoice_line in invoice_lines: + location_dest = claim_line.get_destination_location( + invoice_line.product_id, warehouse) + line = { + 'name': invoice_line.name, + 'claim_origin': "none", + 'invoice_line_id': invoice_line.id, + 'product_id': invoice_line.product_id.id, + 'product_returned_quantity': invoice_line.quantity, + 'unit_sale_price': invoice_line.price_unit, + 'location_dest_id': location_dest.id, + 'state': 'draft', + } + line.update(warranty_values(invoice_line.invoice_id, + invoice_line.product_id)) + claim_lines.append((0, 0, line)) + + value = self._convert_to_cache( + {'claim_line_ids': claim_lines}, validate=False) + self.update(value) + + if self.invoice_id: + self.delivery_address_id = self.invoice_id.partner_id.id + + @api.model + def message_get_reply_to(self): + """ Override to get the reply_to of the parent project. """ + return [claim.section_id.message_get_reply_to()[0] + if claim.section_id else False + for claim in self.sudo()] + + @api.multi + def message_get_suggested_recipients(self): + recipients = super(CrmClaim, self).message_get_suggested_recipients() + try: + for claim in self: + if claim.partner_id: + self._message_add_suggested_recipient( + recipients, claim, + partner=claim.partner_id, reason=_('Customer')) + elif claim.email_from: + self._message_add_suggested_recipient( + recipients, claim, + email=claim.email_from, reason=_('Customer Email')) + except exceptions.AccessError: + # no read access rights -> just ignore suggested recipients + # because this imply modifying followers + pass + return recipients + + def _get_sequence_number(self, code_id): + claim_type_code = self.env['crm.claim.type'].\ + browse(code_id).ir_sequence_id.code + sequence = self.env['ir.sequence'] + + return claim_type_code and sequence.get(claim_type_code) or '/' + + @api.model + def create(self, values): + values = values or {} + if 'code' not in values or not values.get('code') \ + or values.get('code') == '/': + values['code'] = self._get_sequence_number(values['claim_type']) + + return super(CrmClaim, self).create(values) + + @api.multi + def copy(self, default=None): + self.ensure_one() + + default = default or {} + std_default = { + 'code': '/' + } + + std_default.update(default) + return super(CrmClaim, self).copy(default=std_default) diff --git a/crm_claim_rma/models/crm_claim_rma.py b/crm_claim_rma/models/crm_claim_rma.py deleted file mode 100644 index cc8bed76..00000000 --- a/crm_claim_rma/models/crm_claim_rma.py +++ /dev/null @@ -1,527 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Copyright 2015 Eezee-It, MONK Software -# Copyright 2013 Camptocamp -# Copyright 2009-2013 Akretion, -# Author: Emmanuel Samyn, Raphaël Valyi, Sébastien Beau, -# Benoît Guillot, Joel Grand-Guillaume, Leonardo Donelli -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## -import math -import calendar -from datetime import datetime -from dateutil.relativedelta import relativedelta - -from openerp import models, fields, api, exceptions -from openerp.tools.misc import (DEFAULT_SERVER_DATE_FORMAT, - DEFAULT_SERVER_DATETIME_FORMAT) -from openerp.tools.translate import _ - - -class InvoiceNoDate(Exception): - """ Raised when a warranty cannot be computed for a claim line - because the invoice has no date. """ - - -class ProductNoSupplier(Exception): - """ Raised when a warranty cannot be computed for a claim line - because the product has no supplier. """ - - -class SubstateSubstate(models.Model): - """ To precise a state (state=refused; substates= reason 1, 2,...) """ - _name = "substate.substate" - _description = "substate that precise a given state" - - name = fields.Char(string='Sub state', required=True) - substate_descr = fields.Text( - string='Description', - help="To give more information about the sub state") - - -class ClaimLine(models.Model): - """ - Class to handle a product return line (corresponding to one invoice line) - """ - _name = "claim.line" - _description = "List of product to return" - - # Comment written in a claim.line to know about the warranty status - WARRANT_COMMENT = { - 'valid': "Valid", - 'expired': "Expired", - 'not_define': "Not Defined"} - - # Method to calculate total amount of the line : qty*UP - @api.one - def _line_total_amount(self): - self.return_value = (self.unit_sale_price * - self.product_returned_quantity) - - def get_warranty_return_partner(self): - return self.env['product.supplierinfo'].get_warranty_return_partner() - - name = fields.Char(string='Description', required=True, default=None) - claim_origine = fields.Selection( - [('none', 'Not specified'), - ('legal', 'Legal retractation'), - ('cancellation', 'Order cancellation'), - ('damaged', 'Damaged delivered product'), - ('error', 'Shipping error'), - ('exchange', 'Exchange request'), - ('lost', 'Lost during transport'), - ('other', 'Other') - ], - string='Claim Subject', required=True, - help="To describe the line product problem") - claim_descr = fields.Text( - string='Claim description', - help="More precise description of the problem") - product_id = fields.Many2one( - 'product.product', string='Product', help="Returned product") - product_returned_quantity = fields.Float( - string='Quantity', digits=(12, 2), help="Quantity of product returned") - unit_sale_price = fields.Float( - string='Unit sale price', digits=(12, 2), - help="Unit sale price of the product. Auto filled if retrun done " - "by invoice selection. Be careful and check the automatic " - "value as don't take into account previous refunds, invoice " - "discount, can be for 0 if product for free,...") - return_value = fields.Float( - string='Total return', compute='_line_total_amount', - help="Quantity returned * Unit sold price",) - prodlot_id = fields.Many2one( - 'stock.production.lot', - string='Serial/Lot n°', help="The serial/lot of the returned product") - applicable_guarantee = fields.Selection( - [('us', 'Company'), - ('supplier', 'Supplier'), - ('brand', 'Brand manufacturer')], - string='Warranty type') - guarantee_limit = fields.Date( - string='Warranty limit', - readonly=True, - help="The warranty limit is computed as: invoice date + warranty " - "defined on selected product.") - warning = fields.Char( - string='Warranty', - readonly=True, - help="If warranty has expired") - warranty_type = fields.Selection( - get_warranty_return_partner, - string='Warranty type', - readonly=True, - help="Who is in charge of the warranty return treatment towards " - "the end customer. Company will use the current company " - "delivery or default address and so on for supplier and brand" - " manufacturer. Does not necessarily mean that the warranty " - "to be applied is the one of the return partner (ie: can be " - "returned to the company and be under the brand warranty") - warranty_return_partner = fields.Many2one( - 'res.partner', - string='Warranty Address', - help="Where the customer has to send back the product(s)") - claim_id = fields.Many2one( - 'crm.claim', - string='Related claim', - help="To link to the case.claim object") - state = fields.Selection( - [('draft', 'Draft'), - ('refused', 'Refused'), - ('confirmed', 'Confirmed, waiting for product'), - ('in_to_control', 'Received, to control'), - ('in_to_treate', 'Controlled, to treate'), - ('treated', 'Treated')], - string='State', - default="draft") - substate_id = fields.Many2one( - 'substate.substate', - string='Sub state', - help="Select a sub state to precise the standard state. Example 1:" - " state = refused; substate could be warranty over, not in " - "warranty, no problem,... . Example 2: state = to treate; " - "substate could be to refund, to exchange, to repair,...") - last_state_change = fields.Date( - string='Last change', - help="To set the last state / substate change") - invoice_line_id = fields.Many2one( - 'account.invoice.line', - string='Invoice Line', - help='The invoice line related to the returned product') - refund_line_id = fields.Many2one( - 'account.invoice.line', - string='Refund Line', - copy=False, - help='The refund line related to the returned product') - move_in_id = fields.Many2one( - 'stock.move', - string='Move Line from picking in', - copy=False, - help='The move line related to the returned product') - move_out_id = fields.Many2one( - 'stock.move', - string='Move Line from picking out', - copy=False, - help='The move line related to the returned product') - location_dest_id = fields.Many2one( - 'stock.location', - string='Return Stock Location', - help='The return stock location of the returned product') - - @staticmethod - def warranty_limit(start, warranty_duration): - """ Take a duration in float, return the duration in relativedelta - - ``relative_delta(months=...)`` only accepts integers. - We have to extract the decimal part, and then, extend the delta with - days. - """ - decimal_part, months = math.modf(warranty_duration) - months = int(months) - # If we have a decimal part, we add the number them as days to - # the limit. We need to get the month to know the number of - # days. - delta = relativedelta(months=months) - monthday = start + delta - __, days_month = calendar.monthrange(monthday.year, monthday.month) - # ignore the rest of the days (hours) since we expect a date - days = int(days_month * decimal_part) - return start + relativedelta(months=months, days=days) - - def _warranty_limit_values(self, invoice, claim_type, product, claim_date): - if not (invoice and claim_type and product and claim_date): - return {'guarantee_limit': False, 'warning': False} - - date_invoice = invoice.date_invoice - if not date_invoice: - raise InvoiceNoDate - - warning = 'not_define' - date_invoice = datetime.strptime(date_invoice, - DEFAULT_SERVER_DATE_FORMAT) - - if isinstance(claim_type, self.env['crm.claim.type'].__class__): - claim_type = claim_type.id - - if claim_type == self.env.ref('crm_claim_type.' - 'crm_claim_type_supplier').id: - try: - warranty_duration = product.seller_ids[0].warranty_duration - except IndexError: - raise ProductNoSupplier - else: - warranty_duration = product.warranty - - limit = self.warranty_limit(date_invoice, warranty_duration) - if warranty_duration > 0: - claim_date = datetime.strptime(claim_date, - DEFAULT_SERVER_DATETIME_FORMAT) - if limit < claim_date: - warning = 'expired' - else: - warning = 'valid' - - return {'guarantee_limit': limit.strftime(DEFAULT_SERVER_DATE_FORMAT), - 'warning': warning} - - def set_warranty_limit(self): - self.ensure_one() - claim = self.claim_id - try: - values = self._warranty_limit_values( - claim.invoice_id, claim.claim_type, - self.product_id, claim.date) - except InvoiceNoDate: - raise exceptions.Warning( - _('Error'), _('Cannot find any date for invoice. ' - 'Must be a validated invoice.')) - except ProductNoSupplier: - raise exceptions.Warning( - _('Error'), _('The product has no supplier configured.')) - - self.write(values) - return True - - @api.multi - def auto_set_warranty(self): - """ Set warranty automatically - if the user has not himself pressed on 'Calculate warranty state' - button, it sets warranty for him""" - for line in self: - if not line.warning: - line.set_warranty() - return True - - @api.returns('stock.location') - def get_destination_location(self, product, warehouse): - """ - Compute and return the destination location to take - for a return. Always take 'Supplier' one when return type different - from company. - """ - if isinstance(warehouse, int): - location_dest_id = self.env['stock.warehouse']\ - .browse(warehouse).lot_stock_id - else: - location_dest_id = warehouse.lot_stock_id - - if isinstance(product, int): - product = self.env['product.product']\ - .browse(product) - try: - seller = product.seller_ids[0] - if seller.warranty_return_partner != 'company': - location_dest_id = seller.name.property_stock_supplier - finally: - return location_dest_id - - @api.onchange('product_id', 'invoice_line_id') - def _onchange_product_invoice_line(self): - product = self.product_id - invoice_line = self.invoice_line_id - context = self.env.context - - claim = context.get('claim_id') - company_id = context.get('company_id') - warehouse_id = context.get('warehouse_id') - claim_type = context.get('claim_type') - claim_date = context.get('claim_date') - - # claim_exists = not isinstance(claim.id, NewId) - if not claim and not (company_id and warehouse_id and - claim_type and claim_date): - # if we have a claim_id, we get the info from there, - # otherwise we get it from the args (on creation typically) - return False - if not (product and invoice_line): - return False - - invoice = invoice_line.invoice_id - claim_line_model = self.env['claim.line'] - - if claim: - claim = self.env['crm.claim'].browse(claim) - company = claim.company_id - warehouse = claim.warehouse_id - claim_type = claim.claim_type - claim_date = claim.date - else: - warehouse_obj = self.env['stock.warehouse'] - company_obj = self.env['res.company'] - company = company_obj.browse(company_id) - warehouse = warehouse_obj.browse(warehouse_id) - - values = {} - try: - warranty = claim_line_model._warranty_limit_values( - invoice, claim_type, product, claim_date) - except (InvoiceNoDate, ProductNoSupplier): - # we don't mind at this point if the warranty can't be - # computed and we don't want to block the user - values.update({'guarantee_limit': False, 'warning': False}) - else: - values.update(warranty) - warranty_address = claim_line_model._warranty_return_address_values( - product, company, warehouse) - values.update(warranty_address) - self.update(values) - - def _warranty_return_address_values(self, product, company, warehouse): - """ - Return the partner to be used as return destination and - the destination stock location of the line in case of return. - - We can have various cases here: - - company or other: return to company partner or - crm_return_address_id if specified - - supplier: return to the supplier address - """ - if not (product and company and warehouse): - return { - 'warranty_return_partner': False, - 'warranty_type': False, - 'location_dest_id': False - } - sellers = product.seller_ids - if sellers: - seller = sellers[0] - return_address_id = seller.warranty_return_address.id - return_type = seller.warranty_return_partner - else: - # when no supplier is configured, returns to the company - return_address = (company.crm_return_address_id or - company.partner_id) - return_address_id = return_address.id - return_type = 'company' - location_dest = self.get_destination_location(product, warehouse) - return { - 'warranty_return_partner': return_address_id, - 'warranty_type': return_type, - 'location_dest_id': location_dest.id - } - - def set_warranty_return_address(self): - self.ensure_one() - claim = self.claim_id - values = self._warranty_return_address_values( - self.product_id, claim.company_id, claim.warehouse_id) - self.write(values) - return True - - @api.one - def set_warranty(self): - """ Calculate warranty limit and address """ - if not (self.product_id and self.invoice_line_id): - raise exceptions.Warning( - _('Error'), _('Please set product and invoice.')) - self.set_warranty_limit() - self.set_warranty_return_address() - - -# TODO add the option to split the claim_line in order to manage the same -# product separately -class CrmClaim(models.Model): - _inherit = 'crm.claim' - - def _get_default_warehouse(self): - company_id = self.env.user.company_id.id - wh_obj = self.env['stock.warehouse'] - wh = wh_obj.search([('company_id', '=', company_id)], limit=1) - if not wh: - raise exceptions.Warning( - _('There is no warehouse for the current user\'s company.')) - return wh - - @api.one - def name_get(self): - return (self.id, u'[{}] {}'.format(self.code or '', self.name)) - - claim_line_ids = fields.One2many('claim.line', 'claim_id', - string='Claim lines') - planned_revenue = fields.Float(string='Expected revenue') - planned_cost = fields.Float(string='Expected cost') - real_revenue = fields.Float(string='Real revenue') - real_cost = fields.Float(string='Real cost') - invoice_ids = fields.One2many('account.invoice', 'claim_id', - string='Refunds', - copy=False) - picking_ids = fields.One2many('stock.picking', 'claim_id', - string='RMA', - copy=False) - invoice_id = fields.Many2one( - 'account.invoice', - string='Invoice', - help='Related original Customer invoice') - delivery_address_id = fields.Many2one( - 'res.partner', - string='Partner delivery address', - help="This address will be used to deliver repaired or replacement" - "products.") - warehouse_id = fields.Many2one( - 'stock.warehouse', - string='Warehouse', - default=_get_default_warehouse, - required=True) - - @api.onchange('invoice_id', 'warehouse_id', 'claim_type', 'date') - def _onchange_invoice_warehouse_type_date(self): - context = self.env.context - claim_line_obj = self.env['claim.line'] - invoice_lines = self.invoice_id.invoice_line - claim_lines = [] - if not self.warehouse_id: - self.warehouse_id = self._get_default_warehouse() - - claim_type = self.claim_type - claim_date = self.date - warehouse = self.warehouse_id - company = self.company_id - create_lines = context.get('create_lines') - - def warranty_values(invoice, product): - values = {} - try: - warranty = claim_line_obj._warranty_limit_values( - invoice, claim_type, product, claim_date) - except (InvoiceNoDate, ProductNoSupplier): - # we don't mind at this point if the warranty can't be - # computed and we don't want to block the user - values.update({'guarantee_limit': False, 'warning': False}) - else: - values.update(warranty) - - warranty_address = claim_line_obj._warranty_return_address_values( - product, company, warehouse) - values.update(warranty_address) - return values - - if create_lines: # happens when the invoice is changed - for invoice_line in invoice_lines: - location_dest = claim_line_obj.get_destination_location( - invoice_line.product_id, warehouse) - line = { - 'name': invoice_line.name, - 'claim_origine': "none", - 'invoice_line_id': invoice_line.id, - 'product_id': invoice_line.product_id.id, - 'product_returned_quantity': invoice_line.quantity, - 'unit_sale_price': invoice_line.price_unit, - 'location_dest_id': location_dest.id, - 'state': 'draft', - } - line.update(warranty_values(invoice_line.invoice_id, - invoice_line.product_id)) - claim_lines.append((0, 0, line)) - - value = self._convert_to_cache( - {'claim_line_ids': claim_lines}, validate=False) - self.update(value) - - if self.invoice_id: - self.delivery_address_id = self.invoice_id.partner_id.id - - @api.multi - def message_get_reply_to(self): - """ Override to get the reply_to of the parent project. """ - result = {} - for claim in self.sudo(): - section = claim.section_id - if section: - section_reply_to = section.message_get_reply_to() - result[claim.id] = section_reply_to[section.id] - else: - result[claim.id] = False - return result - - @api.multi - def message_get_suggested_recipients(self): - recipients = super(CrmClaim, self).message_get_suggested_recipients() - try: - for claim in self: - if claim.partner_id: - self._message_add_suggested_recipient( - recipients, claim, - partner=claim.partner_id, reason=_('Customer')) - elif claim.email_from: - self._message_add_suggested_recipient( - recipients, claim, - email=claim.email_from, reason=_('Customer Email')) - except exceptions.AccessError: - # no read access rights -> just ignore suggested recipients - # because this imply modifying followers - pass - return recipients diff --git a/crm_claim_rma/models/invoice_no_date.py b/crm_claim_rma/models/invoice_no_date.py new file mode 100644 index 00000000..981c38d4 --- /dev/null +++ b/crm_claim_rma/models/invoice_no_date.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright 2015 Eezee-It, MONK Software, Vauxoo +# Copyright 2013 Camptocamp +# Copyright 2009-2013 Akretion, +# Author: Emmanuel Samyn, Raphaël Valyi, Sébastien Beau, +# Benoît Guillot, Joel Grand-Guillaume, Leonardo Donelli, +# Osval Reyes +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + + +class InvoiceNoDate(Exception): + """ + Raised when a warranty cannot be computed for a claim line + because the invoice has no date. + """ diff --git a/crm_claim_rma/models/product_no_supplier.py b/crm_claim_rma/models/product_no_supplier.py new file mode 100644 index 00000000..7b637406 --- /dev/null +++ b/crm_claim_rma/models/product_no_supplier.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright 2015 Vauxoo +# Copyright 2015 Eezee-It, MONK Software +# Copyright 2013 Camptocamp +# Copyright 2009-2013 Akretion, +# Author: Emmanuel Samyn, Raphaël Valyi, Sébastien Beau, +# Benoît Guillot, Joel Grand-Guillaume, Leonardo Donelli +# Osval Reyes +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + + +class ProductNoSupplier(Exception): + """ + Raised when a warranty cannot be computed for a claim line + because the product has no supplier. + """ diff --git a/crm_claim_rma/models/stock.py b/crm_claim_rma/models/stock_move.py similarity index 75% rename from crm_claim_rma/models/stock.py rename to crm_claim_rma/models/stock_move.py index 616199bb..2cb02204 100644 --- a/crm_claim_rma/models/stock.py +++ b/crm_claim_rma/models/stock_move.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- ############################################################################## # -# Copyright 2015 Eezee-It, MONK Software +# Copyright 2015 Eezee-It, MONK Software, Vauxoo # Copyright 2013 Camptocamp # Copyright 2009-2013 Akretion, # Author: Emmanuel Samyn, Raphaël Valyi, Sébastien Beau, -# Benoît Guillot, Joel Grand-Guillaume, Leonardo Donelli +# Benoît Guillot, Joel Grand-Guillaume, Leonardo Donelli, +# Osval Reyes # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -21,28 +22,15 @@ # along with this program. If not, see . # ############################################################################## - -from openerp import models, fields, api - - -class StockPicking(models.Model): - _inherit = "stock.picking" - - claim_id = fields.Many2one('crm.claim', string='Claim') - - @api.model - @api.returns('self', lambda value: value.id) - def create(self, vals): - if ('name' not in vals) or (vals.get('name') == '/'): - vals['name'] = self.env['ir.sequence'].get(self._name) - return super(StockPicking, self).create(vals) +from openerp import api, models class StockMove(models.Model): - _inherit = "stock.move" + + _name = 'stock.move' + _inherit = ['stock.move', 'mail.thread'] @api.model - @api.returns('self', lambda value: value.id) def create(self, vals): """ In case of a wrong picking out, We need to create a new stock_move in a diff --git a/crm_claim_rma/models/stock_picking.py b/crm_claim_rma/models/stock_picking.py new file mode 100644 index 00000000..7ebfcec5 --- /dev/null +++ b/crm_claim_rma/models/stock_picking.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright 2015 Eezee-It, MONK Software, Vauxoo +# Copyright 2013 Camptocamp +# Copyright 2009-2013 Akretion, +# Author: Emmanuel Samyn, Raphaël Valyi, Sébastien Beau, +# Benoît Guillot, Joel Grand-Guillaume, Leonardo Donelli, +# Osval Reyes +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp import api, fields, models + + +class StockPicking(models.Model): + + _inherit = "stock.picking" + + claim_id = fields.Many2one('crm.claim', 'Claim') + + @api.model + def create(self, vals): + if ('name' not in vals) or (vals.get('name') == '/'): + vals['name'] = self.env['ir.sequence'].get(self._name) + return super(StockPicking, self).create(vals) diff --git a/crm_claim_rma/models/substate_substate.py b/crm_claim_rma/models/substate_substate.py new file mode 100644 index 00000000..235a824e --- /dev/null +++ b/crm_claim_rma/models/substate_substate.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright 2015 Eezee-It, MONK Software, Vauxoo +# Copyright 2013 Camptocamp +# Copyright 2009-2013 Akretion, +# Author: Emmanuel Samyn, Raphaël Valyi, Sébastien Beau, +# Benoît Guillot, Joel Grand-Guillaume, Leonardo Donelli, +# Osval Reyes +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## +from openerp import fields, models + + +class SubstateSubstate(models.Model): + + """ + To precise a state (state=refused; substates= reason 1, 2,...) + """ + + _name = "substate.substate" + _description = "substate that precise a given state" + + name = fields.Char('Sub state', required=True) + substate_descr = fields.Text('Description', + help="To give more " + "information about the sub state") diff --git a/crm_claim_rma/security/ir.model.access.csv b/crm_claim_rma/security/ir.model.access.csv index c053a055..08cd096b 100644 --- a/crm_claim_rma/security/ir.model.access.csv +++ b/crm_claim_rma/security/ir.model.access.csv @@ -1,7 +1,7 @@ -"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" -"access_substate_user","substate.substate.user","model_substate_substate","base.group_sale_salesman_all_leads","True","True","True", -"access_claim_line_user","claim.line.user","model_claim_line","base.group_sale_salesman_all_leads","True","True","True", -"access_substate_manager","substate.substate.manager","model_substate_substate","base.group_sale_manager","True","True","True","True" -"access_claim_line_manager","claim.line.manager","model_claim_line","base.group_sale_manager","True","True","True","True" -"access_substate_user","substate.substate.user","model_substate_substate","base.group_sale_salesman","True","True","True", -"access_claim_line_user","claim.line.user","model_claim_line","base.group_sale_salesman","True","True","True", +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_substate_user,substate.substate.user,model_substate_substate,base.group_sale_salesman_all_leads,1,1,1,0 +access_claim_line_user,claim.line.user,model_claim_line,base.group_sale_salesman_all_leads,1,1,1,0 +access_substate_manager,substate.substate.manager,model_substate_substate,base.group_sale_manager,1,1,1,1 +access_claim_line_manager,claim.line.manager,model_claim_line,base.group_sale_manager,1,1,1,1 +access_substate_user,substate.substate.user,model_substate_substate,base.group_sale_salesman,1,1,1,0 +access_claim_line_user,claim.line.user,model_claim_line,base.group_sale_salesman,1,1,1,0 diff --git a/crm_claim_rma/tests/test_picking_creation.py b/crm_claim_rma/tests/test_picking_creation.py index fecdc1c4..da5c384b 100644 --- a/crm_claim_rma/tests/test_picking_creation.py +++ b/crm_claim_rma/tests/test_picking_creation.py @@ -31,6 +31,7 @@ class TestPickingCreation(common.TransactionCase): super(TestPickingCreation, self).setUp() self.wizard_make_picking = self.env['claim_make_picking.wizard'] + self.stockpicking = self.env['stock.picking'] claim = self.env['crm.claim'] self.product_id = self.env.ref('product.product_product_4') @@ -143,13 +144,45 @@ class TestPickingCreation(common.TransactionCase): self.warehouse_id.lot_stock_id, "Incorrect destination location") - def test_copy(self): - new_claim = self.claim_id.copy() - self.assertNotEqual(new_claim.code, self.claim_id.code) + def create_invoice(self): + sale_order_id = self.env['sale.order'].create({ + 'partner_id': self.ref('base.res_partner_9'), + 'client_order_ref': 'TEST_SO', + 'order_policy': 'manual', + 'order_line': [(0, 0, { + 'product_id': self.ref('product.product_product_8'), + 'product_uom_qty': 2 + })] + }) + sale_order_id.action_button_confirm() + sale_order_id.action_invoice_create() + self.assertTrue(sale_order_id.invoice_ids) + invoice_id = sale_order_id.invoice_ids + invoice_id.signal_workflow('invoice_open') + return invoice_id - def test_mail_thread_recipient(self): - recipients = self.claim_id.message_get_suggested_recipients() - recipients = recipients[self.claim_id.id] - recipient_ids = [r[0] for r in recipients] - self.assertEqual(recipient_ids, - [self.claim_id.partner_id.id]) + def test_03_invoice_refund(self): + claim_id = self.env['crm.claim'].browse( + self.ref('crm_claim.crm_claim_6')) + invoice_id = self.env['account.invoice'].browse( + self.ref('account.invoice_5')) + claim_id.write({ + 'invoice_id': invoice_id.id + }) + claim_id.with_context({'create_lines': True}).\ + _onchange_invoice_warehouse_type_date() + + invoice_refund_wizard_id = self.env['account.invoice.refund'].\ + with_context({ + 'active_ids': [claim_id.invoice_id.id], + 'claim_line_ids': + [[4, cl.id, False] for cl in claim_id.claim_line_ids], + }).create({ + 'description': "Testing Invoice Refund for Claim" + }) + + res = invoice_refund_wizard_id.invoice_refund() + + self.assertTrue(res) + self.assertEquals(res['res_model'], 'account.invoice') + self.assertEquals(eval(res['context'])['type'], 'out_refund') diff --git a/crm_claim_rma/views/claim_line.xml b/crm_claim_rma/views/claim_line.xml new file mode 100644 index 00000000..5ac8d1e3 --- /dev/null +++ b/crm_claim_rma/views/claim_line.xml @@ -0,0 +1,196 @@ + + + + + CRM - Claims Search + claim.line + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CRM - Claims Tree + claim.line + + + + + + + + + + + + +