Merge pull request #86 from cyrilgdn/9.0-crm_claim_rma

crm_claim_rma: migration V9
This commit is contained in:
Yannick Vaucher
2016-08-12 13:59:24 +02:00
committed by GitHub
46 changed files with 8760 additions and 3874 deletions

90
crm_claim_rma/README.rst Normal file
View File

@@ -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 <https://github.com/OCA/rma/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
Credits
=======
Contributors:
-------------
* Emmanuel Samyn <esamyn@gmail.com>
* Sébastien Beau <sebastien.beau@akretion.com.br>
* Benoît Guillot <benoit.guillot@akretion.com.br>
* Joel Grand-Guillaume <joel.grandguillaume@camptocamp.com>
* Guewen Baconnier <guewen.baconnier@camptocamp.com>
* Yannick Vaucher <yannick.vaucher@camptocamp.com>
* Javier Carrasco <javier.carrasco@eezee-it.com>
* Yanina Aular <yanina.aular@vauxoo.com>
* Osval Reyes <osval@vauxoo.com>
* Cyril Gaudin <cyril.gaudin@camptocamp.com>
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.

View File

@@ -1,26 +1,4 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright 2015 Eezee-It
# Copyright 2013 Camptocamp
# Copyright 2009-2013 Akretion,
# Author: Emmanuel Samyn, Raphaël Valyi, Sébastien Beau,
# Joel Grand-Guillaume
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import models
from . import wizards

View File

@@ -1,104 +1,44 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# 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
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
# © 2015 Vauxoo
# © 2015 Eezee-It
# © 2009-2013 Akretion
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
'name': 'RMA Claim (Product Return Management)',
'version': '8.0.1.1.0',
'version': '9.0.1.0.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 <esamyn@gmail.com>
* Sébastien Beau <sebastien.beau@akretion.com.br>
* Benoît Guillot <benoit.guillot@akretion.com.br>
* Joel Grand-Guillaume <joel.grandguillaume@camptocamp.com>
* Guewen Baconnier <guewen.baconnier@camptocamp.com>
* Yannick Vaucher <yannick.vaucher@camptocamp.com>
* Javier Carrasco <javier.carrasco@eezee-it.com>
""",
'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_team.xml',
'data/crm_claim_category.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': [],
'test': [
'test/test_invoice_refund.yml'
],
'installable': False,
'installable': True,
'auto_install': False,
}

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="categ_claim10" model="crm.claim.category">
<field name="name">No Inventory</field>
<field name="team_id" ref="team_after_sales_service"/>
</record>
<record id="categ_claim11" model="crm.claim.category">
<field name="name">Customer Return</field>
<field name="team_id" ref="team_after_sales_service"/>
</record>
<record id="categ_claim12" model="crm.claim.category">
<field name="name">Buyer Cancelled</field>
<field name="team_id" ref="team_after_sales_service"/>
</record>
<record id="categ_claim13" model="crm.claim.category">
<field name="name">General Adjustement</field>
<field name="team_id" ref="team_after_sales_service"/>
</record>
<record id="categ_claim14" model="crm.claim.category">
<field name="name">Could Not Ship</field>
<field name="team_id" ref="team_after_sales_service"/>
</record>
<record id="categ_claim15" model="crm.claim.category">
<field name="name">Different Item</field>
<field name="team_id" ref="team_after_sales_service"/>
</record>
<record id="categ_claim16" model="crm.claim.category">
<field name="name">Merchandise Not Received</field>
<field name="team_id" ref="team_after_sales_service"/>
</record>
<record id="categ_claim17" model="crm.claim.category">
<field name="name">Merchandise Not As Described</field>
<field name="team_id" ref="team_after_sales_service"/>
</record>
<record id="categ_claim18" model="crm.claim.category">
<field name="name">Pricing Error</field>
<field name="team_id" ref="team_after_sales_service"/>
</record>
<record id="categ_claim19" model="crm.claim.category">
<field name="name">Shipping Address Undeliverable</field>
<field name="team_id" ref="team_after_sales_service"/>
</record>
<record id="categ_claim20" model="crm.claim.category">
<field name="name">Delivered Late by Carrier</field>
<field name="team_id" ref="team_after_sales_service"/>
</record>
<record id="categ_claim21" model="crm.claim.category">
<field name="name">Missed Fulfilment Promise</field>
<field name="team_id" ref="team_after_sales_service"/>
</record>
</odoo>

View File

@@ -1,103 +0,0 @@
<?xml version="1.0"?>
<openerp>
<data noupdate="1">
<!-- Claims Sequence n° -->
<record id="seq_type_claim" model="ir.sequence.type">
<field name="name">CRM Claim</field>
<field name="code">crm.claim.rma</field>
</record>
<record id="seq_claim" model="ir.sequence">
<field name="name">CRM Claim</field>
<field name="code">crm.claim.rma</field>
<field eval="5" name="padding"/>
<field name="prefix">RMA-%(year)s/</field>
</record>
<!--
Claim sections
-->
<record model="crm.case.section" id="section_after_sales_service">
<field name="name">After Sales Service</field>
<field name="code">ASV</field>
<field name="parent_id" ref="sales_team.section_sales_department"/>
</record>
<!--
Claim categories
-->
<record model="crm.case.categ" id="categ_claim10">
<field name="name">No Inventory</field>
<field name="section_id" ref="section_after_sales_service"/>
<field name="object_id" search="[('model','=','crm.claim')]" model="ir.model"/>
</record>
<record model="crm.case.categ" id="categ_claim11">
<field name="name">Customer Return</field>
<field name="section_id" ref="section_after_sales_service"/>
<field name="object_id" search="[('model','=','crm.claim')]" model="ir.model"/>
</record>
<record model="crm.case.categ" id="categ_claim12">
<field name="name">Buyer Cancelled</field>
<field name="section_id" ref="section_after_sales_service"/>
<field name="object_id" search="[('model','=','crm.claim')]" model="ir.model"/>
</record>
<record model="crm.case.categ" id="categ_claim13">
<field name="name">General Adjustement</field>
<field name="section_id" ref="section_after_sales_service"/>
<field name="object_id" search="[('model','=','crm.claim')]" model="ir.model"/>
</record>
<record model="crm.case.categ" id="categ_claim14">
<field name="name">Could Not Ship</field>
<field name="section_id" ref="section_after_sales_service"/>
<field name="object_id" search="[('model','=','crm.claim')]" model="ir.model"/>
</record>
<record model="crm.case.categ" id="categ_claim15">
<field name="name">Different Item</field>
<field name="section_id" ref="section_after_sales_service"/>
<field name="object_id" search="[('model','=','crm.claim')]" model="ir.model"/>
</record>
<record model="crm.case.categ" id="categ_claim16">
<field name="name">Merchandise Not Received</field>
<field name="section_id" ref="section_after_sales_service"/>
<field name="object_id" search="[('model','=','crm.claim')]" model="ir.model"/>
</record>
<record model="crm.case.categ" id="categ_claim17">
<field name="name">Merchandise Not As Described</field>
<field name="section_id" ref="section_after_sales_service"/>
<field name="object_id" search="[('model','=','crm.claim')]" model="ir.model"/>
</record>
<record model="crm.case.categ" id="categ_claim18">
<field name="name">Pricing Error</field>
<field name="section_id" ref="section_after_sales_service"/>
<field name="object_id" search="[('model','=','crm.claim')]" model="ir.model"/>
</record>
<record model="crm.case.categ" id="categ_claim19">
<field name="name">Shipping Address Undeliverable</field>
<field name="section_id" ref="section_after_sales_service"/>
<field name="object_id" search="[('model','=','crm.claim')]" model="ir.model"/>
</record>
<record model="crm.case.categ" id="categ_claim20">
<field name="name">Delivered Late by Carrier</field>
<field name="section_id" ref="section_after_sales_service"/>
<field name="object_id" search="[('model','=','crm.claim')]" model="ir.model"/>
</record>
<record model="crm.case.categ" id="categ_claim21">
<field name="name">Missed Fulfilment Promise</field>
<field name="section_id" ref="section_after_sales_service"/>
<field name="object_id" search="[('model','=','crm.claim')]" model="ir.model"/>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="team_after_sales_service" model="crm.team">
<field name="name">After Sales Service</field>
<field name="code">ASV</field>
</record>
</odoo>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="seq_claim" model="ir.sequence">
<field name="name">CRM Claim</field>
<field name="code">crm.claim.rma</field>
<field eval="5" name="padding"/>
<field name="prefix">RMA-%(year)s/</field>
</record>
</odoo>

View File

@@ -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"

1050
crm_claim_rma/i18n/de.po Normal file

File diff suppressed because it is too large Load Diff

1049
crm_claim_rma/i18n/en.po Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1049
crm_claim_rma/i18n/ro.po Normal file

File diff suppressed because it is too large Load Diff

1050
crm_claim_rma/i18n/sl.po Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,12 @@
from . import crm_claim_rma
# -*- coding: utf-8 -*-
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 procurement_group
from . import stock_move
from . import stock_picking
from . import substate_substate

View File

@@ -1,42 +1,23 @@
# -*- 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
# © 2015 Eezee-It, MONK Software, Vauxoo
# © 2013 Camptocamp
# © 2009-2013 Akretion,
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
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
""" 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,44 +40,27 @@ 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 !'))
raise exceptions.UserError(
_('A refund has already been created for this claim !')
)
return [(0, 0, l) for l in new_lines]
@api.model
def _prepare_refund(self, invoice, date=None, period_id=None,
description=None, journal_id=None):
result = super(AccountInvoice, self)._prepare_refund(
invoice, date=date, period_id=period_id, description=description,
journal_id=journal_id)
def _prepare_refund(self, *args, **kwargs):
result = super(AccountInvoice, self)._prepare_refund(*args, **kwargs)
if self.env.context.get('claim_id'):
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

View File

@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# © 2015 Vauxoo
# © 2013 Camptocamp
# © 2009-2013 Akretion,
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
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

View File

@@ -0,0 +1,408 @@
# -*- coding: utf-8 -*-
# © 2015 Vauxoo
# © 2013 Camptocamp
# © 2009-2013 Akretion,
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
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='_compute_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']._columns[
'warranty_return_partner'
].selection
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 _compute_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.UserError(
_('Cannot find any date for invoice. '
'Must be a validated invoice.')
)
except ProductNoSupplier:
raise exceptions.UserError(
_('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.UserError(_('Please set product first'))
if not line_id.invoice_line_id:
raise exceptions.UserError(_('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'].next_by_code('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):
for line_id in self:
line_id.display_name = "%s - %s" % (
line_id.claim_id.code, line_id.name)

View File

@@ -0,0 +1,217 @@
# -*- coding: utf-8 -*-
# © 2015 Eezee-It, MONK Software, Vauxoo
# © 2013 Camptocamp
# © 2009-2013 Akretion,
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
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.UserError(
_('There is no warehouse for the current user\'s company.')
)
return wh
def _get_picking_ids(self):
""" Search all stock_picking associated with this claim.
Either directly with claim_id in stock_picking or through a
procurement_group.
"""
picking_model = self.env['stock.picking']
for claim in self:
claim.picking_ids = picking_model.search([
'|',
('claim_id', '=', claim.id),
('group_id.claim_id', '=', claim.id)
])
@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',
compute=_get_picking_ids,
string='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):
return self.env.ref('crm_claim_type.crm_claim_type_customer')
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']
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 = []
invoices_lines = self.invoice_id.invoice_line_ids.filtered(
lambda line: line.product_id.type in ('consu', 'product')
)
for invoice_line in invoices_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, res_ids, default=None):
""" Override to get the reply_to of the parent project.
"""
results = dict.fromkeys(res_ids, default or False)
if res_ids:
claims = self.browse(res_ids)
results.update({
claim.id: self.env['crm.team'].message_get_reply_to(
[claim.team_id], default
)[claim.team_id] for claim in claims if claim.team_id
})
return results
@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:
claim._message_add_suggested_recipient(
recipients,
partner=claim.partner_id,
reason=_('Customer')
)
elif claim.email_from:
claim._message_add_suggested_recipient(
recipients,
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.next_by_code(
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') == '/':
claim_type = values.get('claim_type')
if not claim_type:
claim_type = self._get_claim_type_default().id
values['code'] = self._get_sequence_number(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)

View File

@@ -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 <http://www.gnu.org/licenses/>.
#
##############################################################################
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

View File

@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
# © 2015 Eezee-It, MONK Software, Vauxoo
# © 2013 Camptocamp
# © 2009-2013 Akretion,
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
class InvoiceNoDate(Exception):
""" Raised when a warranty cannot be computed for a claim line
because the invoice has no date.
"""

View File

@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
# © 2016 Cyril Gaudin (Camptocamp)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import fields, models
class ProcurementGroup(models.Model):
_inherit = 'procurement.group'
claim_id = fields.Many2one('crm.claim', 'Claim')

View File

@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# © 2015 Vauxoo
# © 2015 Eezee-It, MONK Software
# © 2013 Camptocamp
# © 2009-2013 Akretion,
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
class ProductNoSupplier(Exception):
""" Raised when a warranty cannot be computed for a claim line
because the product has no supplier.
"""

View File

@@ -1,58 +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 <http://www.gnu.org/licenses/>.
#
##############################################################################
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)
class StockMove(models.Model):
_inherit = "stock.move"
@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
picking already open.
To avoid having to confirm the stock_move, we override the create and
confirm it at the creation only for this case.
"""
move = super(StockMove, self).create(vals)
if vals.get('picking_id'):
picking = self.env['stock.picking'].browse(vals['picking_id'])
if picking.claim_id and picking.picking_type_id.code == 'incoming':
move.write({'state': 'confirmed'})
return move

View File

@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# © 2015 Eezee-It, MONK Software, Vauxoo
# © 2013 Camptocamp
# © 2009-2013 Akretion,
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import api, models
class StockMove(models.Model):
_name = 'stock.move'
_inherit = ['stock.move', 'mail.thread']
@api.model
def create(self, vals):
""" In case of a wrong picking out,
We need to create a new stock_move in a picking already open.
To avoid having to confirm the stock_move, we override the create and
confirm it at the creation only for this case.
"""
move = super(StockMove, self).create(vals)
if vals.get('picking_id'):
picking = self.env['stock.picking'].browse(vals['picking_id'])
if picking.claim_id and picking.picking_type_id.code == 'incoming':
move.write({'state': 'confirmed'})
return move

View File

@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
# © 2015 Eezee-It, MONK Software, Vauxoo
# © 2013 Camptocamp
# © 2009-2013 Akretion,
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import fields, models
class StockPicking(models.Model):
_inherit = "stock.picking"
claim_id = fields.Many2one('crm.claim', 'Claim')

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# © 2015 Eezee-It, MONK Software, Vauxoo
# © 2013 Camptocamp
# © 2009-2013 Akretion,
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
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")

View File

@@ -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_all_leads,substate.substate.user,model_substate_substate,base.group_sale_salesman_all_leads,1,1,1,0
access_claim_line_user_all_leads,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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_substate_user access_substate_user_all_leads substate.substate.user model_substate_substate base.group_sale_salesman_all_leads True 1 True 1 True 1 0
3 access_claim_line_user access_claim_line_user_all_leads claim.line.user model_claim_line base.group_sale_salesman_all_leads True 1 True 1 True 1 0
4 access_substate_manager substate.substate.manager model_substate_substate base.group_sale_manager True 1 True 1 True 1 True 1
5 access_claim_line_manager claim.line.manager model_claim_line base.group_sale_manager True 1 True 1 True 1 True 1
6 access_substate_user substate.substate.user model_substate_substate base.group_sale_salesman True 1 True 1 True 1 0
7 access_claim_line_user claim.line.user model_claim_line base.group_sale_salesman True 1 True 1 True 1 0

View File

@@ -3,12 +3,11 @@
-
I create a customer invoice
-
!record {model: account.invoice, id: account_invoice_claim_refund, view: account.invoice_form}:
payment_term: account.account_payment_term_advance
journal_id: account.sales_journal
!record {model: account.invoice, id: account_invoice_claim_refund}:
payment_term_id: account.account_payment_term_advance
partner_id: base.res_partner_3
name: 'Test Customer Invoice'
invoice_line:
invoice_line_ids:
- product_id: product.product_product_5
quantity: 10.0
- product_id: product.product_product_4
@@ -54,4 +53,3 @@
refund_lines = self.pool.get('account.invoice.line').browse(cr, uid, refund_line_ids)
assert ref('product.product_product_4') in [refund_lines[0].product_id.id, refund_lines[1].product_id.id], "First line is checked"
assert ref('product.product_product_5') in [refund_lines[0].product_id.id, refund_lines[1].product_id.id], "Second line is checked"

View File

@@ -1,21 +1,4 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Yannick Vaucher
# Copyright 2014 Camptocamp SA
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import test_claim
from . import test_picking_creation

View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# © 2016 Cyril Gaudin (Camptocamp)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp.tests import TransactionCase
class TestClaim(TransactionCase):
def test_create__no_claim_type(self):
# Just test the case when claim_type is not in values and default value
# is not yet filled by BaseModel.create as we override this method
# and our code need claim_type to generate code
claim = self.env['crm.claim'].create({'name': 'Test claim'})
self.assertEqual(
self.env.ref('crm_claim_type.crm_claim_type_customer'),
claim.claim_type,
)
self.assertIsNotNone(claim.code)
self.assertTrue(claim.code.startswith('RMA-C/'))
def test_create__with_claim_type(self):
supplier_type = self.env.ref('crm_claim_type.crm_claim_type_supplier')
claim = self.env['crm.claim'].create({
'name': 'Test claim',
'claim_type': supplier_type.id,
})
self.assertEqual(supplier_type, claim.claim_type)
self.assertIsNotNone(claim.code)
self.assertTrue(claim.code.startswith('RMA-V/'))

View File

@@ -1,25 +1,8 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Yannick Vaucher
# Yanina Aular
# Copyright 2015 Vauxoo
# Copyright 2014 Camptocamp SA
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
# © 2015 Vauxoo
# © 2014 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp.tests import common
@@ -31,21 +14,41 @@ 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')
self.partner_id = self.env.ref('base.res_partner_12')
self.customer_location_id = self.env.ref(
'stock.stock_location_customers')
'stock.stock_location_customers'
)
sale_order_agrolait_demo = self.env.ref('sale.sale_order_1')
self.assertTrue(sale_order_agrolait_demo.invoice_ids,
"The Order Sale of Agrolait not have Invoice")
invoice_agrolait = sale_order_agrolait_demo.invoice_ids[0]
invoice_agrolait.\
signal_workflow('invoice_open')
uom_unit = self.env.ref('product.product_uom_unit')
self.sale_order = self.env['sale.order'].create({
'state': 'done',
'partner_id': self.env.ref('base.res_partner_2').id,
'partner_invoice_id': self.env.ref('base.res_partner_2').id,
'partner_shipping_id': self.env.ref('base.res_partner_2').id,
'pricelist_id': self.env.ref('product.list0').id,
'order_line': [
(0, False, {
'name': product.name,
'product_id': product.id,
'product_uom_qty': qty,
'qty_delivered': qty,
'product_uom': uom_unit.id,
'price_unit': product.list_price
}) for product, qty in [
(self.env.ref('product.product_product_25'), 3),
(self.env.ref('product.product_product_30'), 5),
(self.env.ref('product.product_product_33'), 2),
]
]
})
invoice_id = self.sale_order.action_invoice_create()[0]
self.invoice = self.env['account.invoice'].browse(invoice_id)
# Create the claim with a claim line
self.claim_id = claim.create(
@@ -56,7 +59,7 @@ class TestPickingCreation(common.TransactionCase):
'crm_claim_type_customer').id,
'delivery_address_id': self.partner_id.id,
'partner_id': self.env.ref('base.res_partner_2').id,
'invoice_id': invoice_agrolait.id,
'invoice_id': invoice_id,
})
self.claim_id.with_context({'create_lines': True}).\
_onchange_invoice_warehouse_type_date()
@@ -85,10 +88,11 @@ class TestPickingCreation(common.TransactionCase):
"Incorrect destination location")
def test_01_new_delivery(self):
"""Test wizard creates a correct picking for a new delivery
"""Test wizard creates and runs a procurement for a new delivery
"""
group_model = self.env['procurement.group']
wizardchangeproductqty = self.env['stock.change.product.qty']
wizard_chg_qty = wizardchangeproductqty.with_context({
'active_id': self.product_id.id,
@@ -99,6 +103,9 @@ class TestPickingCreation(common.TransactionCase):
wizard_chg_qty.change_product_qty()
self.assertEqual(0, group_model.search_count([
('claim_id', '=', self.claim_id.id)
]))
wizard = self.wizard_make_picking.with_context({
'active_id': self.claim_id.id,
'partner_id': self.partner_id.id,
@@ -107,29 +114,36 @@ class TestPickingCreation(common.TransactionCase):
}).create({})
wizard.action_create_picking()
procurement_group = group_model.search([
('claim_id', '=', self.claim_id.id)
])
self.assertEqual(1, len(procurement_group))
self.assertEquals(len(self.claim_id.picking_ids), 1,
"Incorrect number of pickings created")
picking = self.claim_id.picking_ids[0]
self.assertEquals(picking.location_id, self.warehouse_id.lot_stock_id,
# Should have 1 procurement by product:
# One on Customer location and one on output
self.assertEqual(3, len(procurement_group.procurement_ids))
# And 2 pickings
self.assertEqual(1, len(self.claim_id.picking_ids))
self.assertEquals(self.warehouse_id.lot_stock_id,
self.claim_id.picking_ids.location_id,
"Incorrect source location")
self.assertEquals(picking.location_dest_id, self.customer_location_id,
self.assertEquals(self.customer_location_id,
self.claim_id.picking_ids.location_dest_id,
"Incorrect destination location")
def test_02_new_product_return(self):
"""Test wizard creates a correct picking for product return
"""
company = self.env.ref('base.main_company')
warehouse_obj = self.env['stock.warehouse']
warehouse_rec = \
warehouse_obj.search([('company_id',
'=', company.id)])[0]
wizard = self.wizard_make_picking.with_context({
'active_id': self.claim_id.id,
'partner_id': self.partner_id.id,
'warehouse_id': self.warehouse_id.id,
'picking_type': warehouse_rec.in_type_id.id,
'picking_type': 'in',
}).create({})
wizard.action_create_picking()
@@ -143,13 +157,54 @@ 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 test_03_invoice_refund(self):
claim_id = self.env['crm.claim'].browse(
self.ref('crm_claim.crm_claim_6')
)
self.invoice.confirm_paid()
claim_id.write({
'invoice_id': self.invoice.id
})
claim_id.with_context({'create_lines': True}).\
_onchange_invoice_warehouse_type_date()
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])
invoice_refund_wizard_id = self.env['account.invoice.refund'].\
with_context({
# Test that invoice_ids is correctly passed as active_ids
'invoice_ids': [claim_id.invoice_id.id],
'claim_line_ids':
[[4, cl.id, False] for cl in claim_id.claim_line_ids],
'description': "Testing Invoice Refund for Claim",
}).create({})
self.assertEqual(
"Testing Invoice Refund for Claim",
invoice_refund_wizard_id.description
)
res = invoice_refund_wizard_id.invoice_refund()
self.assertTrue(res)
self.assertEquals(res['res_model'], 'account.invoice')
self.assertEqual(2, len(res['domain']))
# Second leaf is ('id', 'in', [created_invoice_id])
self.assertEqual(('id', 'in'), res['domain'][1][:2])
self.assertEqual(1, len(res['domain'][1][2]))
refund_invoice = self.env['account.invoice'].browse(
res['domain'][1][2]
)
self.assertEqual('out_refund', refund_invoice.type)
def test_04_display_name(self):
"""
It tests that display_name for each line has a message for it
"""
claim_line_ids = self.env['crm.claim'].browse(
self.ref('crm_claim.crm_claim_6')
)[0].claim_line_ids
all_values = sum([bool(line_id.display_name)
for line_id in claim_line_ids])
self.assertEquals(len(claim_line_ids), all_values)

View File

@@ -1,20 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- INHERITED VIEW FOR THE OBJECT : account_invoice -->
<odoo>
<!-- INHERITED VIEW FOR THE OBJECT : account_invoice -->
<record id="invoice_form" model="ir.ui.view">
<field name="name">crm_claim_rma.invoice_form</field>
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_form" />
<field eval="16" name="priority"/>
<field name="arch" type="xml">
<data>
<xpath expr="/form/sheet/notebook/page[@string='Other Info']/group/group/field[@name='origin']" position="after">
<field name="claim_id" attrs="{'invisible':[('type','!=','out_refund')]}"/>
</xpath>
</data>
</field>
</record>
</data>
</openerp>
<record id="invoice_form" model="ir.ui.view">
<field name="name">crm_claim_rma.invoice_form</field>
<field name="model">account.invoice</field>
<field name="inherit_id" ref="account.invoice_form"/>
<field eval="16" name="priority"/>
<field name="arch" type="xml">
<data>
<field name='origin' position="after">
<field name="claim_id" attrs="{'invisible':[('type','!=','out_refund')]}"/>
</field>
</data>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,199 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_crm_claim_lines_filter" model="ir.ui.view">
<field name="name">CRM - Claims Search</field>
<field name="model">claim.line</field>
<field name="arch" type="xml">
<search string="Search Claims">
<filter icon="terp-check" string="Current" name="current"
domain="[('state','in',('draft', 'refused', 'treated'))]"
separator="1" help="Draft and Open Claims"/>
<filter icon="terp-camera_test"
string="In Progress"
domain="[('state','in',('confirmed','in_to_control','in_to_treate'))]"
separator="1" help="In Progress Claims"/>
<separator orientation="vertical"/>
<field name="number"/>
<field name="state" select='1'/>
<field name="substate_id" select='1'/>
<field name="name" select='1'/>
<field name="warning" select='1'/>
<field name="invoice_line_id" select='1' readonly='1'/>
<field name="product_id" select='1'/>
<field name="prodlot_id" select='1'/>
<field name="move_in_id"/>
<newline/>
<group expand="0" string="More">
<field name="last_state_change" select='1'/>
<field name="guarantee_limit" select='1'/>
<field name="return_value" select='1'/>
<field name="name" select='1'/>
</group>
<newline/>
<group expand="0" string="Group By...">
<filter string="Invoice" icon="terp-dolar"
domain="[]" help="Invoice"
context="{'group_by':'invoice_id'}"/>
<filter string="Product" icon="terp-product"
domain="[]" help="Product"
context="{'group_by':'product_id'}"/>
<separator orientation="vertical"/>
<filter string="Substate" icon="terp-stage"
domain="[]" context="{'group_by':'substate_id'}"/>
<filter string="Claim n°" icon="terp-emblem-documents"
domain="[]" context="{'group_by':'claim_id'}"/>
<filter string="Priority"
name="group_by_priority"
domain="[]"
context="{'group_by':'priority'}"
help="Priority"/>
<filter string="State"
name="group_by_state"
domain="[]"
context="{'group_by':'state'}"
help="Grouping by state"/>
</group>
</search>
</field>
</record>
<record model="ir.ui.view" id="crm_claim_line_tree_view">
<field name="name">CRM - Claims Tree</field>
<field name="model">claim.line</field>
<field name="arch" type="xml">
<tree string="Claim lines">
<field name="claim_id" invisible="1"/>
<field name="number"/>
<field name="state"/>
<field name="substate_id"/>
<field name="product_id"/>
<field name="name"/>
<field name="prodlot_id"/>
<field name="warning"/>
<field name="warranty_type"/>
<field name="warranty_return_partner"/>
<button name="set_warranty" string="Compute Warranty" type="object" icon="gtk-justify-fill"/>
<field name="product_returned_quantity"/>
<field name="claim_origin"/>
<field name="claim_diagnosis"/>
<field name="refund_line_id"/>
<field name="move_in_id"/>
<field name="move_out_id"/>
<field name="priority"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="crm_claim_line_form_view">
<field name="name">CRM - Claim product return line Form</field>
<field name="model">claim.line</field>
<field name="arch" type="xml">
<form string="Claim Line" version="7.0">
<header>
<button name="set_warranty" string="Calculate warranty state" type="object" class="oe_highlight"/>
<field name="state"
widget="statusbar"/>
</header>
<sheet string="Claims">
<div class="oe_title" colspan="4">
<group>
<h1>
<field name="number" class="oe_inline"/>
</h1>
</group>
</div>
<separator string="Problem" colspan="4"/>
<group col="6" colspan="4">
<field name="name" colspan="6"/>
<field name="product_id" readonly="1" colspan="6"/>
<field name="prodlot_id" colspan="6"/>
<field name="claim_origin" colspan="6"/>
<field name="claim_diagnosis" colspan="6"/>
<field name="priority" colspan="6"/>
<field name="product_returned_quantity" invisible="1"/>
<field name="unit_sale_price"/>
<field name="return_value"/>
</group>
<group>
<group string="Warranty">
<field name="guarantee_limit" readonly="1"/>
<field name="warning" readonly="1"/>
<field name="warranty_type" readonly="1"/>
<field name="warranty_return_partner"/>
</group>
<group string="Linked Document">
<field name="claim_id" readonly="1"/>
<field name="invoice_line_id" readonly="1"/>
<field name="refund_line_id" readonly="1"/>
<field name="move_in_id" readonly="1"/>
<field name="move_out_id" readonly="1"/>
</group>
</group>
<separator string="State" colspan="4"/>
<group col="6" colspan="4">
<field name="substate_id" widget='selection'/>
<field name="last_state_change"/>
</group>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
</record>
<!--
A second slightly modified form view to be used for the claim_line_ids
field in the crm.claim form view. Defining it here instead of directly
inside the field allows us to write only the changes and reference it
-->
<record id="crm_claim_line_view_form_embedded" model="ir.ui.view">
<field name="name">Claim line form view to be used inside claim tree</field>
<field name="mode">primary</field>
<field name="model">claim.line</field>
<field name="priority" eval="30"/>
<field name="inherit_id" ref="crm_claim_line_form_view"/>
<field name="arch" type="xml">
<field name="claim_id" position="attributes">
<attribute name="readonly">1</attribute>
</field>
<field name="product_id" position="attributes">
<attribute name="context">{'claim_id': parent.id, 'company_id': parent.company_id, 'warehouse_id':
parent.warehouse_id, 'claim_type': parent.claim_type, 'claim_date': parent.date}
</attribute>
</field>
<field name="invoice_line_id" position="attributes">
<attribute name="context">{'claim_id': parent.id, 'company_id': parent.company_id, 'warehouse_id':
parent.warehouse_id, 'claim_type': parent.claim_type, 'claim_date': parent.date}
</attribute>
</field>
</field>
</record>
<!-- Claim lines action -->
<record model="ir.actions.act_window" id="act_crm_case_claim_lines">
<field name="name">Claim lines</field>
<field name="res_model">claim.line</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="crm_claim_line_tree_view"/>
<field name="search_view_id" ref="view_crm_claim_lines_filter"/>
<field name="context">{'search_default_group_by_priority': True,
'search_default_group_by_state': True}
</field>
</record>
<!-- Menu -->
<menuitem
name="Claim lines"
id="menu_crm_case_claims_claim_lines"
parent="base.menu_aftersale"
action="act_crm_case_claim_lines"
sequence="2"/>
</odoo>

View File

@@ -0,0 +1,291 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!-- CLAIM VIEWS -->
<record model="ir.ui.view" id="crm_case_claims_tree_view">
<field name="name">CRM - Claims Tree</field>
<field name="model">crm.claim</field>
<field name="inherit_id" ref="crm_claim.crm_case_claims_tree_view"/>
<field name="arch" type="xml">
<field name="stage_id" position="after">
<field name="team_id"/>
</field>
</field>
</record>
<!-- Crm claim Search view -->
<record id="view_crm_case_claims_filter" model="ir.ui.view">
<field name="name">CRM - Claims Search</field>
<field name="model">crm.claim</field>
<field name="inherit_id" ref="crm_claim.view_crm_case_claims_filter"/>
<field name="arch" type="xml">
<xpath expr="/search/group/filter[position()=2]" position="after">
<filter string="Sales Team" icon="terp-stock_symbol-selection"
domain="[]"
context="{'group_by':'team_id'}"/>
</xpath>
</field>
</record>
<!-- Right side link to orders -->
<act_window
id="act_crm_claim_rma_sale_orders"
name="Quotations and Sales"
res_model="sale.order"
src_model="crm.claim"/>
<act_window
id="act_crm_claim_rma_purchase_orders"
name="Purchases"
res_model="purchase.order"
src_model="crm.claim"/>
<act_window
domain="[('type', 'in', ('in_invoice', 'out_invoice'))]"
id="act_crm_claim_rma_invoice"
name="Invoices"
res_model="account.invoice"
src_model="crm.claim"/>
<act_window
id="act_crm_claim_rma_refunds"
name="Refunds"
res_model="account.invoice"
src_model="crm.claim"
domain="[('type', 'in', ('in_refund', 'out_refund'))]"/>
<act_window
id="act_crm_claim_rma_picking_in"
name="Incoming Shipment and Returns"
res_model="stock.picking"
src_model="crm.claim"/>
<record model="ir.ui.view" id="crm_claim_rma_form_view">
<field name="name">CRM - Claim product return Form</field>
<field name="model">crm.claim</field>
<field name="inherit_id" ref="crm_claim.crm_case_claims_form_view"/>
<field name="arch" type="xml">
<!-- Header/workflow Buttons -->
<xpath expr="//field[@name='stage_id']" position="before">
<button name="%(action_claim_picking_in)d"
string="New Products Return"
class="oe_inline"
type="action" target="new"
context="{'warehouse_id': warehouse_id,
'partner_id': partner_id}"/>
<button name="%(action_claim_picking_out)d"
class="oe_inline"
string="New Delivery"
type="action" target="new"
context="{'warehouse_id': warehouse_id,
'partner_id': partner_id}"/>
<button name="%(account.action_account_invoice_refund)d"
class="oe_inline"
type='action' string='New Refund'
context="{
'invoice_ids': [invoice_id],
'claim_line_ids': claim_line_ids,
'description': name,
'claim_id': id,
}"/>
</xpath>
<xpath expr="//field[@name='team_id']/parent::group" position="replace">
<group colspan="4" col="4" groups="base.group_user">
<field name="warehouse_id"/>
<field name="date"/>
<field name="user_id"
context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_sale_salesman_all_leads']}"/>
<field name="priority" widget="priority"/>
<field name="date_deadline"/>
<field name="team_id"/>
</group>
</xpath>
<xpath expr="//field[@name='partner_id']" position="attributes">
<attribute name="required">1</attribute>
</xpath>
<xpath expr="//field[@name='partner_phone']" position="attributes">
<attribute name="required">1</attribute>
</xpath>
<xpath expr="//field[@name='email_from']" position="attributes">
<attribute name="required">1</attribute>
</xpath>
<!-- Smart buttons -->
<xpath expr="//field[@name='name']/parent::group" position="before">
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button"
name="%(act_crm_claim_rma_sale_orders)d"
type="action"
icon="fa-strikethrough"
string="Sales"
attrs="{'invisible': ['|',('partner_id','=', False)]}"
context="{'search_default_partner_id': [partner_id], 'search_default_user_id':False}"/>
<button class="oe_stat_button"
name="%(act_crm_claim_rma_purchase_orders)d"
type="action"
icon="fa-shopping-cart"
string="Purchases"
attrs="{'invisible': ['|',('partner_id','=', False)]}"
context="{'search_default_partner_id': [partner_id],
'search_default_user_id':False}"/>
<button class="oe_stat_button"
name="%(act_crm_claim_rma_invoice)d"
type="action"
string="Invoices"
icon="fa-pencil-square"
attrs="{'invisible': ['|',('partner_id','=', False)]}"
context="{'search_default_partner_id': [partner_id], 'group_by':'type', 'search_default_user_id':False}"/>
<button class="oe_stat_button"
name="%(act_crm_claim_rma_refunds)d"
type="action"
string="Refunds"
icon="fa-file-text"
attrs="{'invisible': ['|',('partner_id','=', False), ('claim_type','in', ['customer','other'])]}"
context="{'search_default_partner_id': [partner_id], 'group_by':'type', 'search_default_user_id':False}"/>
<button class="oe_stat_button"
icon="fa-reply"
name="%(act_crm_claim_rma_picking_in)d"
type="action"
string="Returns"
attrs="{'invisible': ['|',('partner_id','=', False), ('claim_type','in', ['supplier','other'])]}"
context="{'search_default_claim_id': active_id, 'search_default_user_id':False}"/>
</div>
</xpath>
<xpath expr="//field[@name='name']/parent::group" position="replace">
<div class="oe_title oe_left">
<h1>
<field name="code"/>
</h1>
<div class="oe_edit_only">
<label for="name"/>
</div>
<h1>
<small>
<field name="name"/>
</small>
</h1>
</div>
</xpath>
<field name="email_from" position="after">
<field name="pick" string="Pick the product in the store"/>
<field name="delivery_address_id"
string="Partner delivery address"
domain="[('id','child_of',partner_id)]"
attrs="{'invisible': ['|',('pick','=', True)],
'required': [('pick','=', False)]}"
context="{'tree_view_ref': 'crm_claim_rma.view_partner_contact_tree',
'search_default_parent_id': partner_id}"/>
<field name="invoice_id"
domain="['|',('commercial_partner_id','=',partner_id),('partner_id','=',partner_id)]"
context="{'create_lines': True}"/>
</field>
<xpath expr="//field[@name='email_from']/parent::group" position="after">
<div name="serial">
<!-- Place for mass return button from crm_rma_lot_mass_return -->
</div>
<separator colspan="2" string="Product Returns"/>
<field name="claim_line_ids">
<tree string="Returned lines" create="false">
<field name="claim_id" invisible="1"/>
<field name="state"/>
<field name="product_id"/>
<field name="name"/>
<field name="prodlot_id"/>
<field name="invoice_line_id" invisible="True"/>
<field name="warning"/>
<field name="warranty_type"/>
<button name="set_warranty" string="Compute Warranty"
type="object" icon="gtk-justify-fill"/>
<field name="product_returned_quantity"/>
<field name="claim_origin"/>
<field name="claim_diagnosis"/>
<field name="location_dest_id" invisible="True"/>
</tree>
</field>
</xpath>
<!-- New tabs for products return and generated documents -->
<xpath expr="//field[@name='email_from']/ancestor::page" position="after">
<page string="Generated Documents">
<separator colspan="2" string="Refunds"/>
<field name="invoice_ids" colspan="4" readonly="1"/>
<separator colspan="2" string="Receptions / Deliveries"/>
<field name="picking_ids" colspan="4" readonly="1"/>
</page>
</xpath>
</field>
</record>
<record model="ir.actions.act_window" id="crm_claim.crm_claim_category_claim0">
<field name="context">{"search_default_user_id":uid, "stage_type":'claim'}</field>
</record>
<!-- substates action -->
<record id="act_crm_claim_substates" model="ir.actions.act_window">
<field name="name">Claim line substates</field>
<field name="res_model">substate.substate</field>
<field name="view_type">form</field>
</record>
<menuitem
name="Claim line substates"
id="menu_crm_case_claims_claim_line_substates"
parent="crm_claim.menu_config_claim"
action="act_crm_claim_substates"
sequence="2"/>
<!-- Update account invoice !-->
<record id="crm_claim_rma_form" model="ir.ui.view">
<field name="name">CRM Claim RMA</field>
<field name="model">crm.claim</field>
<field name="inherit_id" ref="crm_claim.crm_case_claims_form_view"/>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='date_deadline']" position="after">
<field name="rma_number"/>
</xpath>
</data>
</field>
</record>
<record id="search_crm_claim_rma_number" model="ir.ui.view">
<field name="name">CRM - Claims Search</field>
<field name="model">crm.claim</field>
<field name="inherit_id" ref="crm_claim.view_crm_case_claims_filter"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='name']" position="after">
<field name="rma_number"/>
</xpath>
</field>
</record>
<record id="tree_crm_claim_rma_number" model="ir.ui.view">
<field name="name">CRM - Claims Tree</field>
<field name="model">crm.claim</field>
<field name="inherit_id" ref="crm_claim.crm_case_claims_tree_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='name']" position="after">
<field name="rma_number"/>
</xpath>
</field>
</record>
<record model="ir.ui.view" id="crm_case_claims_form_view">
<field name="name">CRM - Claims Form</field>
<field name="model">crm.claim</field>
<field name="inherit_id" ref="crm_claim_type.crm_case_claims_form_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='claim_type']" position="attributes">
<attribute name="context">{'create_lines': False}</attribute>
</xpath>
</field>
</record>
</odoo>

View File

@@ -1,409 +0,0 @@
<?xml version="1.0"?>
<openerp>
<data>
<!-- Claim line views -->
<!-- SEARCH -->
<record id="view_crm_claim_lines_filter" model="ir.ui.view">
<field name="name">CRM - Claims Search</field>
<field name="model">claim.line</field>
<field name="arch" type="xml">
<search string="Search Claims">
<filter icon="terp-check" string="Current" name="current"
domain="[('state','in',('draft', 'refused', 'treated'))]"
separator="1" help="Draft and Open Claims" />
<filter icon="terp-camera_test"
string="In Progress"
domain="[('state','in',('confirmed','in_to_control','in_to_treate'))]"
separator="1" help="In Progress Claims"/>
<separator orientation="vertical"/>
<field name="state" select='1'/>
<field name="substate_id" select='1'/>
<field name="name" select='1'/>
<field name="warning" select='1'/>
<field name="invoice_line_id" select='1'/>
<field name="product_id" select='1'/>
<field name="prodlot_id" select='1'/>
<newline/>
<group expand="0" string="More">
<field name="last_state_change" select='1'/>
<field name="guarantee_limit" select='1'/>
<field name="return_value" select='1'/>
<field name="name" select='1'/>
</group>
<newline/>
<group expand="0" string="Group By...">
<filter string="Invoice" icon="terp-dolar"
domain="[]" help="Invoice"
context="{'group_by':'invoice_id'}"/>
<filter string="Product" icon="terp-product"
domain="[]" help="Product"
context="{'group_by':'product_id'}"/>
<separator orientation="vertical"/>
<filter string="Substate" icon="terp-stage"
domain="[]" context="{'group_by':'substate_id'}"/>
<filter string="Claim n°" icon="terp-emblem-documents"
domain="[]" context="{'group_by':'claim_id'}"/>
</group>
</search>
</field>
</record>
<!-- TREE -->
<record model="ir.ui.view" id="crm_claim_line_tree_view">
<field name="name">CRM - Claims Tree</field>
<field name="model">claim.line</field>
<field name="arch" type="xml">
<tree string="Claim lines">
<field name="claim_id" invisible="1"/>
<field name="state"/>
<field name="substate_id"/>
<field name="product_id"/>
<field name="name"/>
<field name="prodlot_id"/>
<field name="warning"/>
<field name="warranty_type"/>
<field name="warranty_return_partner"/>
<button name="set_warranty" string="Compute Waranty" type="object" icon="gtk-justify-fill"/>
<field name="product_returned_quantity"/>
<field name="claim_origine"/>
<field name="refund_line_id"/>
<field name="move_in_id"/>
<field name="move_out_id"/>
</tree>
</field>
</record>
<!-- FORM -->
<record model="ir.ui.view" id="crm_claim_line_form_view">
<field name="name">CRM - Claim product return line Form</field>
<field name="model">claim.line</field>
<field name="arch" type="xml">
<form string="Claim Line" version="7.0">
<header>
<button name="set_warranty" string="Calculate warranty state" type="object" class="oe_highlight"/>
</header>
<sheet string="Claims">
<div class="oe_title">
<group>
<label for="name" class="oe_edit_only"/>
<h1><field name="name"/></h1>
</group>
</div>
<group>
<group string="Returned good">
<field name="product_returned_quantity"/>
<field name="product_id" />
<field name="prodlot_id"/>
<field name="unit_sale_price"/>
<field name="return_value"/>
</group>
<group string="Linked Document">
<field name="claim_id"/>
<field name="invoice_line_id"/>
<field name="refund_line_id"/>
<field name="move_in_id"/>
<field name="move_out_id"/>
</group>
</group>
<group>
<group string="Problem">
<field name="claim_origine" nolabel="1" colspan="4"/>
<field name="claim_descr" nolabel="1" colspan="4"/>
</group>
<group string="Warranty">
<field name="guarantee_limit"/>
<field name="warning"/>
<field name="warranty_return_partner"/>
<field name="warranty_type"/>
</group>
</group>
<separator string="State" colspan="4"/>
<group col="6" colspan="4">
<field name="state"/>
<field name="substate_id" widget='selection'/>
<field name="last_state_change"/>
</group>
</sheet>
</form>
</field>
</record>
<!--
A second slightly modified form view to be used for the claim_line_ids
field in the crm.claim form view. Defining it here instead of directly
inside the field allows us to write only the changes and reference it
-->
<record id="crm_claim_line_view_form_embedded" model="ir.ui.view">
<field name="name">Claim line form view to be used inside claim tree</field>
<field name="mode">primary</field>
<field name="model">claim.line</field>
<field name="priority" eval="30"/>
<field name="inherit_id" ref="crm_claim_line_form_view"/>
<field name="arch" type="xml">
<field name="claim_id" position="attributes">
<attribute name="readonly">1</attribute>
</field>
<field name="product_id" position="attributes">
<attribute name="context">{'claim_id': parent.id, 'company_id': parent.company_id, 'warehouse_id': parent.warehouse_id, 'claim_type': parent.claim_type, 'claim_date': parent.date}</attribute>
</field>
<field name="invoice_line_id" position="attributes">
<attribute name="context">{'claim_id': parent.id, 'company_id': parent.company_id, 'warehouse_id': parent.warehouse_id, 'claim_type': parent.claim_type, 'claim_date': parent.date}</attribute>
</field>
</field>
</record>
<!-- CLAIM VIEWS -->
<record model="ir.ui.view" id="crm_case_claims_tree_view">
<field name="name">CRM - Claims Tree</field>
<field name="model">crm.claim</field>
<field name="inherit_id" ref="crm_claim.crm_case_claims_tree_view"/>
<field name="arch" type="xml">
<field name="stage_id" position="after">
<field name="section_id"/>
</field>
</field>
</record>
<!-- Crm claim Search view -->
<record id="view_crm_case_claims_filter" model="ir.ui.view">
<field name="name">CRM - Claims Search</field>
<field name="model">crm.claim</field>
<field name="inherit_id" ref="crm_claim.view_crm_case_claims_filter"/>
<field name="arch" type="xml">
<filter string="Stage" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}" position="before">
<filter string="Sales Team" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by':'section_id'}"/>
</filter>
</field>
</record>
<!-- Right side link to orders -->
<act_window
id="act_crm_claim_rma_sale_orders"
name="Quotations and Sales"
res_model="sale.order"
src_model="crm.claim"/>
<!-- Right side link to invoices -->
<act_window
domain="[('type', '=', 'out_invoice')]"
id="act_crm_claim_rma_invoice_out"
name="Customer Invoices"
res_model="account.invoice"
src_model="crm.claim"/>
<!-- Right side link to invoices -->
<act_window
domain="[('type', '=', 'in_invoice')]"
id="act_crm_claim_rma_invoice_in"
name="Supplier Invoices"
res_model="account.invoice"
src_model="crm.claim"/>
<!-- Right side link to refunds -->
<act_window
domain="[('type', '=', 'out_refund')]"
id="act_crm_claim_rma_refunds_out"
name="Customer Refunds"
res_model="account.invoice"
src_model="crm.claim"/>
<!-- Right side link to refunds -->
<act_window
domain="[('type', '=', 'in_refund')]"
id="act_crm_claim_rma_refunds_in"
name="Supplier Refunds"
res_model="account.invoice"
src_model="crm.claim"/>
<!-- Right side link to picking in -->
<act_window
domain="[('picking_type_id.code', '=', 'incoming')]"
id="act_crm_claim_rma_picking_in"
name="Incoming Shipment and Returns"
res_model="stock.picking"
src_model="crm.claim"/>
<!-- Right side link to picking out -->
<act_window
domain="[('picking_type_id.code', '=', 'outgoing')]"
id="act_crm_claim_rma_picking_out"
name="Deliveries"
res_model="stock.picking"
src_model="crm.claim"/>
<record model="ir.actions.act_window" id="crm_claim.crm_case_categ_claim0">
<field name="context">{"search_default_user_id":uid, "stage_type":'claim'}</field>
</record>
<!-- Claim lines action -->
<record model="ir.actions.act_window" id="act_crm_case_claim_lines">
<field name="name">Claim lines</field>
<field name="res_model">claim.line</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="crm_claim_line_tree_view"/>
<field name="search_view_id" ref="view_crm_claim_lines_filter"/>
</record>
<!-- substates action -->
<record id="act_crm_claim_substates" model="ir.actions.act_window">
<field name="name">Claim line substates</field>
<field name="res_model">substate.substate</field>
<field name="view_type">form</field>
</record>
<!-- Menu -->
<menuitem
name="Claim lines"
id="menu_crm_case_claims_claim_lines"
parent="base.menu_aftersale"
action="act_crm_case_claim_lines"
sequence="2"/>
<menuitem
name="Claim line substates"
id="menu_crm_case_claims_claim_line_substates"
parent="crm_claim.menu_config_claim"
action="act_crm_claim_substates"
sequence="2"/>
<record model="ir.ui.view" id="crm_case_claims_form_view">
<field name="name">CRM - Claims Form</field>
<field name="model">crm.claim</field>
<field name="inherit_id" ref="crm_claim_type.crm_case_claims_form_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='claim_type']" position="attributes">
<attribute name="context">{'create_lines': False}</attribute>
</xpath>
</field>
</record>
<record model="ir.ui.view" id="crm_claim_rma_form_view">
<field name="name">CRM - Claims Form</field>
<field name="model">crm.claim</field>
<field name="inherit_id" ref="crm_claim.crm_case_claims_form_view"/>
<field name="arch" type="xml">
<!-- Header/workflow Buttons -->
<field name="stage_id" position="before">
<button
name="%(action_claim_picking_in)d"
type="action" string="New Products Return"
attrs="{'invisible': [('partner_id','=', False)]}"
context="{'warehouse_id': warehouse_id, 'partner_id': partner_id}"/>
<button
name="%(action_claim_picking_out)d"
type="action"
string="New Delivery"
attrs="{'invisible': [('partner_id','=', False)]}"
context="{'warehouse_id': warehouse_id, 'partner_id': partner_id}"/>
<button
name="%(account.action_account_invoice_refund)d"
type='action'
string='New Refund'
attrs="{'invisible': [('invoice_id','=', False)]}"
context="{'invoice_ids': [invoice_id], 'claim_line_ids': claim_line_ids, 'description': name, 'claim_id': id}"/>
</field>
<field name="date_deadline" position="after">
<field name="warehouse_id" context="{'create_lines': False}"/>
</field>
<field name="date" position="replace"/>
<field name="user_id" position="before">
<field name="date" context="{'create_lines': False}"/>
</field>
<!-- Smart buttons -->
<xpath expr="//sheet[@string='Claims']/group[1]" position="after">
<div class="oe_right oe_button_box" style="width: 300px;" name="buttons">
<button name="%(act_crm_claim_rma_sale_orders)d" type="action"
string="Quotation/Sales"
icon="fa-strikethrough"
class="oe_stat_button"
attrs="{'invisible': ['|',('partner_id','=', False),('claim_type','in',
[%(crm_claim_type.crm_claim_type_supplier)d,%(crm_claim_type.crm_claim_type_other)d])]}"
context="{'search_default_partner_id': [partner_id],'search_default_user_id':False}"/>
<button name="%(act_crm_claim_rma_invoice_out)d" type="action"
string="Invoices"
icon="fa-pencil-square-o"
class="oe_stat_button"
attrs="{'invisible': ['|',('partner_id','=', False),('claim_type','in',
[%(crm_claim_type.crm_claim_type_supplier)d,%(crm_claim_type.crm_claim_type_other)d])]}"
context="{'search_default_partner_id': [partner_id],'search_default_user_id':False}"/>
<button name="%(act_crm_claim_rma_refunds_out)d" type="action"
string="Refunds"
icon="fa-file-text-o"
class="oe_stat_button"
attrs="{'invisible': ['|',('partner_id','=', False),('claim_type','in',
[%(crm_claim_type.crm_claim_type_supplier)d,%(crm_claim_type.crm_claim_type_other)d])]}"
context="{'search_default_partner_id': [partner_id],'search_default_user_id':False}"/>
<button name="%(act_crm_claim_rma_invoice_in)d" type="action"
string="Invoices"
icon="fa-pencil-square-o"
class="oe_stat_button"
attrs="{'invisible': ['|',('partner_id','=', False),('claim_type','in',
[%(crm_claim_type.crm_claim_type_customer)d,%(crm_claim_type.crm_claim_type_other)d])]}"
context="{'search_default_partner_id': [partner_id],'search_default_user_id':False}"/>
<button name="%(act_crm_claim_rma_refunds_in)d" type="action"
string="Refunds"
icon="fa-file-text-o"
class="oe_stat_button"
attrs="{'invisible': ['|',('partner_id','=', False),('claim_type','in',
[%(crm_claim_type.crm_claim_type_customer)d,%(crm_claim_type.crm_claim_type_other)d])]}"
context="{'search_default_partner_id': [partner_id],'search_default_user_id':False}"/>
<button name="%(act_crm_claim_rma_picking_in)d" type="action"
string="Products"
icon="fa-reply"
class="oe_stat_button"
attrs="{'invisible': ['|',('partner_id','=', False),('claim_type','in',
[%(crm_claim_type.crm_claim_type_customer)d,%(crm_claim_type.crm_claim_type_other)d])]}"
context="{'search_default_claim_id': active_id,'search_default_user_id':False}"/>
<button name="%(act_crm_claim_rma_picking_out)d" type="action"
string="Deliveries"
icon="fa-truck"
class="oe_stat_button"
attrs="{'invisible': ['|',('partner_id','=', False),('claim_type','in',
[%(crm_claim_type.crm_claim_type_supplier)d,%(crm_claim_type.crm_claim_type_other)d])]}"
context="{'search_default_partner_id': [partner_id],'search_default_user_id':False}"/>
</div>
</xpath>
<xpath expr="//sheet[@string='Claims']/group[1]" position="replace">
<div class="oe_title oe_left">
<h1><field name="code"/></h1>
<div class="oe_edit_only">
<label for="name"/>
</div>
<h1><small><field name="name"/></small></h1>
</div>
</xpath>
<!-- Remove domain and widget attributes -->
<field name="categ_id" widget="selection" position="replace">
<field name="categ_id"/>
</field>
<!-- New tabs for products return and generated documents -->
<page string="Follow Up" position="before">
<page string="Product Return">
<group name="Product Return">
<separator string="Product Return" colspan="4"/>
<group>
<field name="company_id" invisible="1"/>
<field name="invoice_id" domain="['|',('commercial_partner_id','=',partner_id),('partner_id','=',partner_id)]" context="{'create_lines': True}"/>
<field name="delivery_address_id" context="{'tree_view_ref': 'crm_claim_rma.view_partner_contact_tree', 'search_default_parent_id': partner_id}"/>
</group>
<div name="serial">
<!-- Place for mass return button from crm_rma_lot_mass_return -->
</div>
<field
name="claim_line_ids"
nolabel="1"
context="{'default_claim_id': active_id, 'form_view_ref': 'crm_claim_rma.crm_claim_line_view_form_embedded'}"/>
</group>
</page>
<page string="Generated Documents">
<separator colspan="2" string="Refunds"/>
<field name="invoice_ids" colspan="4" readonly="1"/>
<separator colspan="2" string="Receptions / Deliveries"/>
<field name="picking_ids" colspan="4" readonly="1"/>
</page>
</page>
</field>
</record>
</data>
</openerp>

View File

@@ -1,24 +1,22 @@
<?xml version="1.0"?>
<openerp>
<data>
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!-- Address list with details to help shipping address selection -->
<record id="view_partner_contact_tree" model="ir.ui.view">
<field name="name">res.partner.contact.tree</field>
<field name="model">res.partner</field>
<field name="arch" type="xml">
<tree string="Contacts">
<field name="name"/>
<field name="function" invisible="1"/>
<field name="street"/>
<field name="zip"/>
<field name="city"/>
<field name="email"/>
<field name="user_id" invisible="1"/>
<field name="is_company" invisible="1"/>
<field name="country_id"/>
<field name="parent_id" invisible="1"/>
</tree>
</field>
<field name="name">res.partner.contact.tree</field>
<field name="model">res.partner</field>
<field name="arch" type="xml">
<tree string="Contacts">
<field name="name"/>
<field name="function" invisible="True"/>
<field name="street"/>
<field name="zip"/>
<field name="city"/>
<field name="email"/>
<field name="user_id" invisible="True"/>
<field name="is_company" invisible="True"/>
<field name="country_id"/>
<field name="parent_id" invisible="True"/>
</tree>
</field>
</record>
</data>
</openerp>
</odoo>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- INHERITED VIEW FOR THE OBJECT : stock_picking -->
<record id="picking_in_form" model="ir.ui.view">
<field name="name">crm_claim_rma.picking_in_form</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='move_type']" position="after">
<field name="claim_id"/>
</xpath>
</field>
</record>
<record id="stock_move_mail_thread" model="ir.ui.view">
<field name="name">stock_move_mail_thread</field>
<field name="model">stock.move</field>
<field name="inherit_id" ref="stock.view_move_form"/>
<field name="arch" type="xml">
<xpath expr="//sheet" position="after">
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</xpath>
</field>
</record>
<!-- SEARCH -->
<record id="view_picking_internal_search_claim_id" model="ir.ui.view">
<field name="name">stock.picking.internal.search</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_internal_search"/>
<field name="arch" type="xml">
<xpath expr="//search" position="inside">
<field name="claim_id" help="Moves created from claims"/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -1,26 +1,4 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# 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
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import claim_make_picking
from . import account_invoice_refund

View File

@@ -1,28 +1,11 @@
# -*- 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
# © 2015 Vauxoo
# © 2015 Eezee-It, MONK Software
# © 2013 Camptocamp
# © 2009-2013 Akretion,
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields, api
from openerp import api, fields, models
class AccountInvoiceRefund(models.TransientModel):
@@ -33,8 +16,9 @@ class AccountInvoiceRefund(models.TransientModel):
description = fields.Char(default=_default_description)
@api.one
@api.multi
def compute_refund(self, mode='refund'):
self.ensure_one()
invoice_ids = self.env.context.get('invoice_ids', [])
if invoice_ids:
self = self.with_context(active_ids=invoice_ids)

View File

@@ -1,26 +1,9 @@
# -*- 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
# © 2015 Eezee-It, MONK Software
# © 2013 Camptocamp
# © 2009-2013 Akretion,
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import time
from openerp import models, fields, exceptions, api, _
@@ -33,8 +16,7 @@ class ClaimMakePicking(models.TransientModel):
@api.returns('stock.location')
def _get_common_dest_location_from_line(self, lines):
"""
If all the lines have the same destination location return that,
""" If all the lines have the same destination location return that,
else return an empty recordset
"""
dests = lines.mapped('location_dest_id')
@@ -43,8 +25,7 @@ class ClaimMakePicking(models.TransientModel):
@api.returns('res.partner')
def _get_common_partner_from_line(self, lines):
"""
If all the lines have the same warranty return partner return that,
""" If all the lines have the same warranty return partner return that,
else return an empty recordset
"""
partners = lines.mapped('warranty_return_partner')
@@ -55,13 +36,8 @@ class ClaimMakePicking(models.TransientModel):
def _default_claim_line_source_location_id(self):
picking_type = self.env.context.get('picking_type')
partner_id = self.env.context.get('partner_id')
warehouse_id = self.env.context.get('warehouse_id')
if picking_type == 'out' and warehouse_id:
return self.env['stock.warehouse'].browse(
warehouse_id).lot_stock_id
if partner_id:
if picking_type == 'in' and partner_id:
partner = self.env['res.partner'].browse(partner_id)
return partner.property_stock_customer
@@ -78,11 +54,6 @@ class ClaimMakePicking(models.TransientModel):
picking_type = self.env.context.get('picking_type')
partner_id = self.env.context.get('partner_id')
if isinstance(picking_type, int):
picking_obj = self.env['stock.picking.type']
return picking_obj.browse(picking_type)\
.default_location_dest_id
if picking_type == 'out' and partner_id:
return self.env['res.partner'].browse(
partner_id).property_stock_customer
@@ -100,35 +71,28 @@ class ClaimMakePicking(models.TransientModel):
# TODO use custom states to show buttons of this wizard or not instead
# of raise an error
picking_type = self.env.context.get('picking_type')
if isinstance(picking_type, int):
picking_obj = self.env['stock.picking.type']
if picking_obj.\
browse(picking_type).\
code == 'incoming':
picking_type = 'in'
else:
picking_type = 'out'
move_field = ('move_in_id'
if picking_type == 'in'
else 'move_out_id')
move_field = 'move_in_id' if picking_type == 'in' else 'move_out_id'
domain = [('claim_id', '=', self.env.context['active_id'])]
lines = self.env['claim.line'].\
search(domain)
if lines:
domain = domain + [
'|', (move_field, '=', False),
(move_field+'.state', '=', 'cancel')
]
domain = domain + ['|', (move_field, '=', False),
(move_field + '.state', '=', 'cancel')]
lines = lines.search(domain)
if not lines:
raise exceptions.Warning(
_('Error'),
_('A picking has already been created for this claim.'))
raise exceptions.UserError(
_('A picking has already been created for this claim.')
)
return lines
delivery_warehouse_id = fields.Many2one(
'stock.warehouse', string='Source Warehouse',
default=lambda self: self.env.context.get('warehouse_id'),
help="Warehouse where to take the replacement products for customers.",
)
claim_line_source_location_id = fields.Many2one(
'stock.location', string='Source Location', required=True,
'stock.location', string='Source Location',
default=_default_claim_line_source_location_id,
help="Location where the returned products are from.")
@@ -159,7 +123,6 @@ class ClaimMakePicking(models.TransientModel):
'state': 'draft',
'date': time.strftime(DT_FORMAT),
'partner_id': partner_id,
'invoice_state': "none",
'company_id': claim.company_id.id,
'location_id': self.claim_line_source_location_id.id,
'location_dest_id': self.claim_line_dest_location_id.id,
@@ -186,34 +149,17 @@ class ClaimMakePicking(models.TransientModel):
'note': self._get_picking_note(),
}
@api.multi
def action_create_picking(self):
context = self._context
picking_type = self.env.context.get('picking_type')
if isinstance(picking_type, int):
picking_obj = self.env['stock.picking.type']
picking_type_rec = picking_obj.browse(picking_type)
if picking_type_rec.code == 'incoming':
picking_type = 'in'
elif picking_type_rec.code == 'outgoing':
picking_type = 'out'
else:
picking_type = 'int'
warehouse_obj = self.env['stock.warehouse']
warehouse_rec = warehouse_obj.browse(context.get('warehouse_id'))
if picking_type == 'out':
picking_type = warehouse_rec.out_type_id
write_field = 'move_out_id'
elif picking_type == 'in':
def _create_picking(self, claim, picking_type):
warehouse_rec = self.env['stock.warehouse'].browse(
self.env.context.get('warehouse_id')
)
if picking_type == 'in':
picking_type = warehouse_rec.in_type_id
write_field = 'move_in_id'
else:
picking_type = warehouse_rec.int_type_id
write_field = 'move_out_id'
claim = self.env['crm.claim'].browse(self.env.context['active_id'])
partner_id = claim.delivery_address_id.id
claim_lines = self.claim_line_ids
@@ -224,21 +170,21 @@ class ClaimMakePicking(models.TransientModel):
common_dest_location = self._get_common_dest_location_from_line(
claim_lines)
if not common_dest_location:
raise Warning(
_('Error'),
raise exceptions.UserError(
_('A product return cannot be created for various '
'destination locations, please choose line with a '
'same destination location.'))
'same destination location.')
)
claim_lines.auto_set_warranty()
common_dest_partner = self._get_common_partner_from_line(
claim_lines)
if not common_dest_partner:
raise exceptions.Warning(
_('Error'),
raise exceptions.UserError(
_('A product return cannot be created for various '
'destination addresses, please choose line with a '
'same address.'))
'same address.')
)
partner_id = common_dest_partner.id
# create picking
@@ -258,10 +204,9 @@ class ClaimMakePicking(models.TransientModel):
domain = ("[('picking_type_id', '=', %s), ('partner_id', '=', %s)]" %
(picking_type.id, partner_id))
view_obj = self.env['ir.ui.view']
view_id = view_obj.search([('model', '=', 'stock.picking'),
('type', '=', 'form'),
])[0]
view_id = self.env['ir.ui.view'].search(
[('model', '=', 'stock.picking'),
('type', '=', 'form')])[0]
return {
'name': self._get_picking_name(),
'view_type': 'form',
@@ -273,6 +218,44 @@ class ClaimMakePicking(models.TransientModel):
'type': 'ir.actions.act_window',
}
def _create_procurement(self, claim):
""" Create a procurement order for each line in this claim and put
all procurements in a procurement group linked to this claim.
:type claim: crm_claim
"""
group = self.env['procurement.group'].create({
'name': claim.code,
'claim_id': claim.id,
'move_type': 'direct',
})
for line in self.claim_line_ids:
procurement = self.env['procurement.order'].create({
'name': line.product_id.name_template,
'group_id': group.id,
'origin': claim.code,
'warehouse_id': self.delivery_warehouse_id.id,
'date_planned': time.strftime(DT_FORMAT),
'product_id': line.product_id.id,
'product_qty': line.product_returned_quantity,
'product_uom': line.product_id.product_tmpl_id.uom_id.id,
'location_id': self.claim_line_dest_location_id.id,
'company_id': claim.company_id.id,
})
procurement.run()
@api.multi
def action_create_picking(self):
claim = self.env['crm.claim'].browse(self.env.context['active_id'])
picking_type = self.env.context.get('picking_type')
if picking_type == 'out':
return self._create_procurement(claim)
else:
return self._create_picking(claim, picking_type)
@api.multi
def action_cancel(self):
return {'type': 'ir.actions.act_window_close'}

View File

@@ -5,54 +5,54 @@
Copyright (C) 2011 Akretion Benoît GUILLOT <benoit.guillot@akretion.com>
The licence is in the file __openerp__.py
-->
<openerp>
<data>
<record id="view_claim_picking" model="ir.ui.view">
<field name="name">claim_picking</field>
<field name="model">claim_make_picking.wizard</field>
<field name="arch" type="xml">
<form string="Select exchange lines to add in picking" version="7.0">
<group name="locations" string="Locations">
<field name="claim_line_source_location_id"/>
<field name="claim_line_dest_location_id"/>
</group>
<separator string="Select lines for picking"/>
<field name="claim_line_ids" nolabel="1"/>
<footer>
<button
<odoo>
<record id="view_claim_picking" model="ir.ui.view">
<field name="name">claim_picking</field>
<field name="model">claim_make_picking.wizard</field>
<field name="arch" type="xml">
<form string="Select exchange lines to add in picking">
<group name="locations" string="Locations">
<field name="delivery_warehouse_id" invisible="context.get('picking_type') != 'out'"
required="context.get('picking_type') == 'out'"/>
<field name="claim_line_source_location_id" invisible="context.get('picking_type') == 'out'"
required="context.get('picking_type') != 'out'"/>
<field name="claim_line_dest_location_id"/>
</group>
<separator string="Select lines for picking"/>
<field name="claim_line_ids" nolabel="1"/>
<footer>
<button
string="Create picking"
name="action_create_picking" type="object"
class="oe_highlight"/>
or
<button name="action_cancel"
string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
or
<button name="action_cancel"
string="Cancel" class="oe_link" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="action_claim_picking_in" model="ir.actions.act_window">
<field name="name">Return Products</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">claim_make_picking.wizard</field>
<field name="src_model">crm.claim</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="context">{'picking_type': 'in','product_return': True}</field>
</record>
<record id="action_claim_picking_in" model="ir.actions.act_window">
<field name="name">Return Products</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">claim_make_picking.wizard</field>
<field name="src_model">crm.claim</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="context">{'picking_type': 'in','product_return': True}</field>
</record>
<record id="action_claim_picking_out" model="ir.actions.act_window">
<field name="name">Create Outgoing Shipments</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">claim_make_picking.wizard</field>
<field name="src_model">crm.claim</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="context">{'picking_type': 'out'}</field>
</record>
<record id="action_claim_picking_out" model="ir.actions.act_window">
<field name="name">Create Outgoing Shipments</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">claim_make_picking.wizard</field>
<field name="src_model">crm.claim</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="context">{'picking_type': 'out'}</field>
</record>
</data>
</openerp>
</odoo>