[FIX] pep8, review

This commit is contained in:
Guewen Baconnier
2013-12-03 09:29:41 +01:00
parent 07a2e3c768
commit 05bc2a662f
6 changed files with 436 additions and 371 deletions

View File

@@ -27,41 +27,49 @@
Management of Return Merchandise Authorization (RMA)
====================================================
This module aim 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 introduce 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).
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 contain the following features:
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
* access to related customer data (orders, invoices, refunds, picking
in/out) from a claim
Using this module make the logistic flow of return this way:
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)
* 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
""",
'author': 'Akretion, Camptocamp',
'website': 'http://www.akretion.com, http://www.camptocamp.com',
'depends': ['sale','stock','crm_claim','product_warranty'],
'data': [
'wizard/claim_make_picking_view.xml',
'crm_claim_rma_view.xml',
'security/ir.model.access.csv',
'account_invoice_view.xml',
'stock_view.xml',
'crm_claim_rma_data.xml',
],
'images': ['images/product_return.png', 'images/claim.png','images/return_line.png','images/exchange.png'],
'depends': ['sale',
'stock',
'crm_claim',
'product_warranty',
],
'data': ['wizard/claim_make_picking_view.xml',
'crm_claim_rma_view.xml',
'security/ir.model.access.csv',
'account_invoice_view.xml',
'stock_view.xml',
'crm_claim_rma_data.xml',
],
'images': ['images/product_return.png',
'images/claim.png',
'images/return_line.png',
'images/exchange.png',
],
'installable': True,
'active': False,
'auto_install': False,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@@ -19,7 +19,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import fields, orm, osv
from openerp.osv import fields, orm
from tools.translate import _
@@ -32,59 +32,60 @@ class account_invoice(orm.Model):
}
def _refund_cleanup_lines(self, cr, uid, lines, context=None):
"""Override when from claim to update the quantity and link
to the claim line."""
if context is None: context = {}
new_lines = []
# check if is an invoice_line and we are from a claim
if context.get('claim_line_ids') and lines and lines[0]._name =='account.invoice.line' :
for claim_line_id in context.get('claim_line_ids'):
claim_info = self.pool.get('claim.line').read(cr, uid,
claim_line_id[1],
[
'invoice_line_id',
'product_returned_quantity',
'refund_line_id'],
context=context)
if not claim_info['refund_line_id']:
#For each lines replace quantity and add clain_line_id
inv_line_obj = self.pool.get('account.invoice.line')
inv_line = inv_line_obj.browse(cr, uid,
[claim_info['invoice_line_id'][0]],
context=context)[0]
clean_line = {}
for field in inv_line._all_columns.keys():
column_type = inv_line._all_columns[field].column._type
if column_type == 'many2one':
clean_line[field] = inv_line[field].id
elif column_type not in ['many2many','one2many']:
clean_line[field] = inv_line[field]
elif field == 'invoice_line_tax_id':
tax_list = []
for tax in inv_line[field]:
tax_list.append(tax.id)
clean_line[field] = [(6,0, tax_list)]
clean_line['quantity'] = claim_info['product_returned_quantity']
clean_line['claim_line_id'] = [claim_line_id[1]]
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 osv.except_osv(_('Error !'),
_('A refund has already been created for this claim !'))
else:
return super(account_invoice, self)._refund_cleanup_lines(cr, uid, lines, context=None)
return map(lambda x: (0,0,x), new_lines)
def _prepare_refund(self, cr, uid, invoice, date=None, period_id=None,
description=None, journal_id=None, context=None):
""" Override when from claim to update the quantity and link to the
claim line."""
if context is None:
context={}
result = super(account_invoice, self)._prepare_refund(cr, uid, invoice,
date=date, period_id=period_id, description=description,
context = {}
new_lines = []
inv_line_obj = self.pool.get('account.invoice.line')
claim_line_obj = self.pool.get('claim.line')
# check if is an invoice_line and we are from a claim
if not (context.get('claim_line_ids') and lines and
lines[0]._name =='account.invoice.line'):
return super(account_invoice, self)._refund_cleanup_lines(
cr, uid, lines, context=None)
for __, claim_line_id in context.get('claim_line_ids'):
line = claim_line_obj.browse(cr, uid, claim_line_id,
context=context)
if not line.refund_line_id:
# For each lines replace quantity and add claim_line_id
inv_line = inv_line_obj.browse(cr, uid,
line.invoice_line_id.id,
context=context)
clean_line = {}
for field_name, field in inv_line._all_columns.iteritems():
column_type = field.column._type
if column_type == 'many2one':
clean_line[field_name] = inv_line[field_name].id
elif column_type not in ('many2many', 'one2many'):
clean_line[field_name] = inv_line[field_name]
elif field_name == 'invoice_line_tax_id':
tax_list = []
for tax in inv_line[field_name]:
tax_list.append(tax.id)
clean_line[field_name] = [(6, 0, tax_list)]
clean_line['quantity'] = 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 orm.except_orm(
_('Error !'),
_('A refund has already been created for this claim !'))
return [(0, 0, line) for line in new_lines]
def _prepare_refund(self, cr, uid, invoice, date=None, period_id=None,
description=None, journal_id=None, context=None):
if context is None:
context = {}
result = super(account_invoice, self)._prepare_refund(
cr, uid, invoice,
date=date, period_id=period_id, description=description,
journal_id=journal_id, context=context)
if context.get('claim_id'):
result['claim_id'] = context.get('claim_id')
result['claim_id'] = context['claim_id']
return result
@@ -97,10 +98,11 @@ class account_invoice_line(orm.Model):
if vals.get('claim_line_id'):
claim_line_id = vals['claim_line_id']
del vals['claim_line_id']
line_id = super(account_invoice_line, self).create(cr, uid,
vals, context=context)
line_id = super(account_invoice_line, self).create(
cr, uid, vals, context=context)
if claim_line_id:
claim_line_obj = self.pool.get('claim.line')
claim_line_obj.write(cr, uid, claim_line_id,
{'refund_line_id': line_id}, context=context)
claim_line_obj.write(cr, uid, claim_line_id,
{'refund_line_id': line_id},
context=context)
return line_id

View File

@@ -2,7 +2,7 @@
##############################################################################
#
# Copyright 2013 Camptocamp
# Copyright 2009-2013 Akretion,
# 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
@@ -20,28 +20,24 @@
#
##############################################################################
from openerp.osv import fields, orm, osv
# from crm import crm
import time
from openerp.osv import fields, orm
from datetime import datetime
from dateutil.relativedelta import relativedelta
import time
from tools.translate import _
from tools import DEFAULT_SERVER_DATE_FORMAT
from tools.translate import _
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
from openerp.tools.translate import _
class substate_substate(orm.Model):
"""
To precise a state (state=refused; substates= reason 1, 2,...)
"""
class substate_substate(orm.Model):
""" To precise a state (state=refused; substates= reason 1, 2,...) """
_name = "substate.substate"
_description = "substate that precise a given state"
_columns = {
'name': fields.char('Sub state', size=128, required=True),
'substate_descr' : fields.text('Description',
help="To give more information about the sub state"),
# ADD OBJECT TO FILTER
}
'name': fields.char('Sub state', required=True),
'substate_descr': fields.text(
'Description',
help="To give more information about the sub state"),
}
class claim_line(orm.Model):
@@ -50,18 +46,19 @@ class claim_line(orm.Model):
"""
_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
def _line_total_amount(self, cr, uid, ids, field_name, arg, context=None):
res = {}
for line in self.browse(cr,uid,ids):
res[line.id] = line.unit_sale_price*line.product_returned_quantity
for line in self.browse(cr, uid, ids, context=context):
res[line.id] = (line.unit_sale_price *
line.product_returned_quantity)
return res
def copy_data(self, cr, uid, id, default=None, context=None):
@@ -75,134 +72,158 @@ class claim_line(orm.Model):
std_default.update(default)
return super(claim_line, self).copy_data(
cr, uid, id, default=std_default, context=context)
def get_warranty_return_partner(self, cr, uid, context=None):
seller = self.pool.get('product.supplierinfo')
result = seller.get_warranty_return_partner(cr, uid, context=context)
return result
_columns = {
'name': fields.char('Description', size=64,required=True),
'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')],
'name': fields.char('Description', required=True),
'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')
],
'Claim Subject',
required=True,
help="To describe the line product problem"),
'claim_descr' : fields.text('Claim description',
help="More precise description of the problem"),
'product_id': fields.many2one('product.product', 'Product',
'claim_descr': fields.text(
'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('Quantity', digits=(12,2),
'product_returned_quantity': fields.float(
'Quantity', digits=(12, 2),
help="Quantity of product returned"),
'unit_sale_price' : fields.float('Unit sale price', digits=(12,2),
help="Unit sale price of the product. Auto filed if retrun done by"
" invoice selection. BE CAREFUL AND CHECK the automatic value "
"as don't take into account previous refounds, invoice "
'unit_sale_price' : fields.float(
'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.function(_line_total_amount, method=True,
string='Total return',
type='float',
'return_value' : fields.function(
_line_total_amount, string='Total return', type='float',
help="Quantity returned * Unit sold price",),
'prodlot_id': fields.many2one('stock.production.lot', 'Serial/Lot n°',
'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')],
[('us', 'Company'),
('supplier', 'Supplier'),
('brand', 'Brand manufacturer')],
'Warranty type'),
'guarantee_limit': fields.date('Warranty limit',
'guarantee_limit': fields.date(
'Warranty limit',
readonly=True,
help="The warranty limit is computed as: invoice date + warranty "
"defined on selected product."),
'warning': fields.char('Warranty', size=64,
'warning': fields.char(
'Warranty',
readonly=True,
help="If warranty has expired"),
"warranty_type": fields.selection(get_warranty_return_partner,
"warranty_type": fields.selection(
get_warranty_return_partner,
'Warranty type',
readonly=True,
help="Who is in charge of the warranty return treatment toward the end customer. "
"Company will use the current compagny delivery or default address and so on for "
"supplier and brand manufacturer. Doesn't necessarly 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',
'Warranty Address',
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', 'Related claim',
'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')], 'State'),
'substate_id': fields.many2one('substate.substate', 'Sub state',
'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'),
'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('Last change',
'last_state_change': fields.date(
string='Last change',
help="To set the last state / substate change"),
'invoice_line_id': fields.many2one('account.invoice.line',
'Invoice Line',
'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',
'Refund Line',
'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',
'Move Line from picking in',
'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',
'Move Line from picking out',
'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',
'Return Stock Location',
'location_dest_id': fields.many2one(
'stock.location',
string='Return Stock Location',
help='The return stock location of the returned product'),
}
_defaults = {
'state': lambda *a: 'draft',
'name': lambda *a: 'none',
}
'state': 'draft',
'name': 'none',
}
# Method to calculate warranty limit
def set_warranty_limit(self, cr, uid, ids, claim_line, context=None):
date_invoice = claim_line.invoice_line_id.invoice_id.date_invoice
if date_invoice:
warning = _(self.WARRANT_COMMENT['not_define'])
date_inv_at_server = datetime.strptime(date_invoice,
DEFAULT_SERVER_DATE_FORMAT)
supplier = claim_line.product_id.seller_ids[0]
if claim_line.claim_id.claim_type == 'supplier':
waranty_duration = int(supplier.warranty_duration)
else:
waranty_duration = int(claim_line.product_id.warranty)
limit = (date_inv_at_server +
relativedelta(months=waranty_duration)).strftime(DEFAULT_SERVER_DATE_FORMAT)
# If waranty period was defined
if waranty_duration > 0:
if limit < claim_line.claim_id.date:
warning = _(self.WARRANT_COMMENT['expired'])
else:
warning = _(self.WARRANT_COMMENT['valid'])
self.write(cr,uid,ids,{
'guarantee_limit' : limit,
'warning' : warning,
})
if not date_invoice:
raise orm.except_orm(
_('Error !'),
_('Cannot find any date for invoice. '
'Must be a validated invoice.'))
warning = _(self.WARRANT_COMMENT['not_define'])
date_inv_at_server = datetime.strptime(date_invoice,
DEFAULT_SERVER_DATE_FORMAT)
supplier = claim_line.product_id.seller_ids[0]
if claim_line.claim_id.claim_type == 'supplier':
warranty_duration = int(supplier.warranty_duration)
else:
raise osv.except_osv(_('Error !'),
_('Cannot find any date for invoice ! Must be a validated invoice !'))
warranty_duration = int(claim_line.product_id.warranty)
limit = date_inv_at_server + relativedelta(months=warranty_duration)
# If waranty period was defined
if warranty_duration > 0:
claim_date = datetime.strptime(claim_line.claim_id.date,
DEFAULT_SERVER_DATE_FORMAT)
if limit < claim_date:
warning = _(self.WARRANT_COMMENT['expired'])
else:
warning = _(self.WARRANT_COMMENT['valid'])
self.write(cr, uid, ids,
{'guarantee_limit': limit.strftime(DEFAULT_SERVER_DATE_FORMAT),
'warning': warning},
context=context)
return True
def get_destination_location(self, cr, uid, product_id,
warehouse_id, context=None):
def get_destination_location(self, cr, uid, product_id,
warehouse_id, context=None):
"""Compute and return the destination location ID to take
for a return. Always take 'Supplier' one when return type different
from company."""
@@ -220,41 +241,44 @@ class claim_line(orm.Model):
return location_dest_id
# Method to calculate warranty return address
def set_warranty_return_address(self, cr, uid, ids,
claim_line, context=None):
def set_warranty_return_address(self, cr, uid, ids, claim_line, context=None):
"""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 case here:
- company or other: return to company partner or crm_return_address_id
if specified
- supplier: return to the supplier address"""
- supplier: return to the supplier address
"""
return_address = None
seller = claim_line.product_id.seller_info_id
claim_company = claim_line.claim_id.company_id
return_address = seller.warranty_return_address.id
return_type = seller.warranty_return_partner
location_dest_id = self.get_destination_location(cr, uid,
claim_line.product_id.id,
location_dest_id = self.get_destination_location(
cr, uid, claim_line.product_id.id,
claim_line.claim_id.warehouse_id.id,
context=context)
self.write(cr, uid, ids,
{'warranty_return_partner': return_address,
'warranty_type': return_type,
'location_dest_id': location_dest_id})
{'warranty_return_partner': return_address,
'warranty_type': return_type,
'location_dest_id': location_dest_id},
context=context)
return True
# Method to calculate warranty limit and address
def set_warranty(self, cr, uid, ids, context=None):
""" Calculate warranty limit and address """
for claim_line in self.browse(cr, uid, ids, context=context):
if claim_line.product_id and claim_line.invoice_line_id:
self.set_warranty_limit(cr, uid, ids,
claim_line, context=context)
self.set_warranty_return_address(cr, uid, ids,
claim_line, context=context)
else:
raise osv.except_osv(_('Error !'),
_('PLEASE SET PRODUCT & INVOICE!'))
return True
if not (claim_line.product_id and claim_line.invoice_line_id):
raise orm.except_orm(
_('Error !'),
_('Please set product and invoice.'))
self.set_warranty_limit(cr, uid, ids,
claim_line, context=context)
self.set_warranty_return_address(cr, uid, ids,
claim_line, context=context)
return True
#TODO add the option to split the claim_line in order to manage the same product separately
@@ -262,18 +286,22 @@ class crm_claim(orm.Model):
_inherit = 'crm.claim'
def _get_sequence_number(self, cr, uid, context=None):
res = self.pool.get('ir.sequence').get(cr, uid,
'crm.claim.rma', context=context) or '/'
seq_obj = self.pool.get('ir.sequence')
res = seq_obj.get(cr, uid, 'crm.claim.rma', context=context) or '/'
return res
def _get_default_warehouse(self, cr, uid, context=None):
company_id = self.pool.get('res.users').browse(cr, uid, uid,
context=context).company_id.id
wh_ids = self.pool.get('stock.warehouse').search(cr, uid,
[('company_id','=',company_id)], context=context)
user_obj = self.pool.get('res.users')
user = user_obj.browse(cr, uid, uid, context=context)
company_id = user.company_id.id
wh_obj = self.pool.get('stock.warehouse')
wh_ids = wh_obj.search(cr, uid,
[('company_id', '=', company_id)],
context=context)
if not wh_ids:
raise osv.except_osv(_('Error!'),
_('There is no warehouse for the current user\'s company!'))
raise orm.except_orm(
_('Error!'),
_('There is no warehouse for the current user\'s company.'))
return wh_ids[0]
def name_get(self, cr, uid, ids, context=None):
@@ -283,9 +311,9 @@ class crm_claim(orm.Model):
return res
def create(self, cr, uid, vals, context=None):
if ('number' not in vals) or (vals.get('number')=='/'):
if ('number' not in vals) or (vals.get('number') == '/'):
vals['number'] = self._get_sequence_number(cr, uid, context=context)
new_id = super(crm_claim, self).create(cr, uid, vals, context)
new_id = super(crm_claim, self).create(cr, uid, vals, context=context)
return new_id
def copy_data(self, cr, uid, id, default=None, context=None):
@@ -301,49 +329,55 @@ class crm_claim(orm.Model):
cr, uid, id, default=std_default, context=context)
_columns = {
'number': fields.char('Number', readonly=True,
'number': fields.char(
'Number', readonly=True,
states={'draft': [('readonly', False)]},
required=True,
select=True,
help="Company internal claim unique number"),
'claim_type': fields.selection([('customer','Customer'),
('supplier','Supplier'),
('other','Other')],
'Claim type',
'claim_type': fields.selection(
[('customer', 'Customer'),
('supplier', 'Supplier'),
('other', 'Other')],
string='Claim type',
required=True,
help="customer = from customer to company ; supplier = from "
"company to supplier"),
'claim_line_ids' : fields.one2many('claim.line', 'claim_id',
'Return lines'),
help="Customer: from customer to company.\n "
"Supplier: from company to supplier."),
'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 revenue'),
'real_cost': fields.float('Real cost'),
'invoice_ids': fields.one2many('account.invoice', 'claim_id', 'Refunds'),
'picking_ids': fields.one2many('stock.picking', 'claim_id', 'RMA'),
'invoice_id': fields.many2one('account.invoice', 'Invoice',
'invoice_id': fields.many2one(
'account.invoice', string='Invoice',
help='Related original Cusotmer invoice'),
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse',
'warehouse_id': fields.many2one(
'stock.warehouse', string='Warehouse',
required=True),
}
_defaults = {
'number': lambda self, cr, uid, context: '/',
'number': '/',
'claim_type': 'customer',
'warehouse_id': _get_default_warehouse,
}
_sql_constraints = [
('number_uniq', 'unique(number, company_id)', 'Number/Reference must be unique per Company!'),
('number_uniq', 'unique(number, company_id)',
'Number/Reference must be unique per Company!'),
]
def onchange_partner_address_id(self, cr, uid, ids, add,
email=False, context=None):
res = super(crm_claim, self).onchange_partner_address_id(cr, uid, ids,
add, email=email)
def onchange_partner_address_id(self, cr, uid, ids, add, email=False, context=None):
res = super(crm_claim, self).onchange_partner_address_id(
cr, uid, ids, add, email=email)
if add:
if not res['value']['email_from'] or not res['value']['partner_phone']:
address = self.pool.get('res.partner').browse(cr, uid, add)
partner_obj = self.pool.get('res.partner')
address = partner_obj.browse(cr, uid, add, context=context)
for other_add in address.partner_id.address:
if other_add.email and not res['value']['email_from']:
res['value']['email_from'] = other_add.email
@@ -351,29 +385,30 @@ class crm_claim(orm.Model):
res['value']['partner_phone'] = other_add.phone
return res
def onchange_invoice_id(self, cr, uid, ids, invoice_id,
warehouse_id, context=None):
def onchange_invoice_id(self, cr, uid, ids, invoice_id, warehouse_id, context=None):
invoice_line_obj = self.pool.get('account.invoice.line')
claim_line_obj = self.pool.get('claim.line')
invoice_line_ids = invoice_line_obj.search(cr, uid,
[('invoice_id','=',invoice_id)])
invoice_line_ids = invoice_line_obj.search(
cr, uid,
[('invoice_id', '=', invoice_id)],
context=context)
claim_lines = []
if not warehouse_id:
warehouse_id = self._get_default_warehouse(cr, uid, context=context)
for invoice_line in invoice_line_obj.browse(cr,uid,invoice_line_ids):
location_dest_id = claim_line_obj.get_destination_location(cr, uid,
invoice_line.product_id.id,
warehouse_id,
context=context)
invoice_lines = invoice_line_obj.browse(cr, uid, invoice_line_ids,
context=context)
for invoice_line in invoice_lines:
location_dest_id = claim_line_obj.get_destination_location(
cr, uid, invoice_line.product_id.id,
warehouse_id, context=context)
claim_lines.append({
'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',
})
return {'value' : {'claim_line_ids' : claim_lines}}
'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',
})
return {'value': {'claim_line_ids': claim_lines}}

View File

@@ -19,7 +19,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import fields, orm, osv
from openerp.osv import fields, orm
class stock_picking(orm.Model):
@@ -30,24 +30,24 @@ class stock_picking(orm.Model):
'claim_id': fields.many2one('crm.claim', 'Claim'),
}
def create(self, cr, user, vals, context=None):
if ('name' not in vals) or (vals.get('name')=='/'):
if vals['type'] != 'internal':
seq_obj_name = 'stock.picking.' + vals['type']
def create(self, cr, uid, vals, context=None):
if ('name' not in vals) or (vals.get('name') == '/'):
sequence_obj = self.pool.get('ir.sequence')
if vals['type'] == 'internal':
seq_obj_name = self._name
else:
seq_obj_name = self._name
vals['name'] = self.pool.get('ir.sequence').get(cr, user,
seq_obj_name,
context=context)
new_id = super(stock_picking, self).create(cr, user, vals,
context=context)
seq_obj_name = 'stock.picking.' + vals['type']
vals['name'] = sequence_obj.get(cr, uid, seq_obj_name,
context=context)
new_id = super(stock_picking, self).create(cr, uid, vals,
context=context)
return new_id
class stock_picking_out(orm.Model):
_inherit = "stock.picking.out"
_columns = {
'claim_id': fields.many2one('crm.claim', 'Claim'),
}
@@ -56,26 +56,27 @@ class stock_picking_out(orm.Model):
class stock_picking_out(orm.Model):
_inherit = "stock.picking.in"
_columns = {
'claim_id': fields.many2one('crm.claim', 'Claim'),
}
#This part concern the case of a wrong picking out. We need to create a new
#stock_move in a picking already open.
#In order to don't have to confirm the stock_move we override the create and
#confirm it at the creation only for this case
# This part concern the case of a wrong picking out. We need to create a new
# stock_move in a picking already open.
# In order to don't have to confirm the stock_move we override the create and
# confirm it at the creation only for this case
class stock_move(orm.Model):
_inherit = "stock.move"
def create(self, cr, uid, vals, context=None):
move_id = super(stock_move, self).create(cr, uid, vals, context=context)
if vals.get('picking_id'):
picking = self.pool.get('stock.picking').browse(cr, uid,
vals['picking_id'], context=context)
picking_obj = self.pool.get('stock.picking')
picking = picking_obj.browse(cr, uid, vals['picking_id'],
context=context)
if picking.claim_id and picking.type == u'in':
move = self.write(cr, uid, move_id, {'state': 'confirmed'},
context=context)
self.write(cr, uid, move_id, {'state': 'confirmed'},
context=context)
return move_id

View File

@@ -19,26 +19,27 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.osv import fields, orm
from openerp.osv import orm
class account_invoice_refund(orm.TransientModel):
_inherit = "account.invoice.refund"
def compute_refund(self, cr, uid, ids, mode='refund', context=None):
if context is None: context={}
if context is None:
context={}
if context.get('invoice_ids'):
context['active_ids'] = context.get('invoice_ids')
return super(account_invoice_refund, self).compute_refund(cr, uid, ids,
mode='refund', context=context)
return super(account_invoice_refund, self).compute_refund(
cr, uid, ids, mode=mode, context=context)
def _get_description(self, cr, uid, context=None):
if context is None: context = {}
if context is None:
context = {}
description = context.get('description') or ''
return description
_defaults = {
'description': _get_description,
}

View File

@@ -2,7 +2,7 @@
##############################################################################
#
# Copyright 2013 Camptocamp
# Copyright 2009-2013 Akretion,
# 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
@@ -20,7 +20,7 @@
#
##############################################################################
from openerp.osv import fields, orm, osv
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
from openerp import netsvc
from openerp.tools.translate import _
import time
@@ -31,54 +31,62 @@ class claim_make_picking(orm.TransientModel):
_name = 'claim_make_picking.wizard'
_description = 'Wizard to create pickings from claim lines'
_columns = {
'claim_line_source_location': fields.many2one('stock.location',
'Source Location',
'claim_line_source_location': fields.many2one(
'stock.location',
string='Source Location',
help="Location where the returned products are from.",
required=True),
'claim_line_dest_location': fields.many2one('stock.location',
'Dest. Location',
'claim_line_dest_location': fields.many2one(
'stock.location',
string='Dest. Location',
help="Location where the system will stock the returned products.",
required=True),
'claim_line_ids': fields.many2many('claim.line',
'claim_line_ids': fields.many2many(
'claim.line',
'claim_line_picking',
'claim_picking_id',
'claim_line_id',
'Claim lines'),
string='Claim lines'),
}
def _get_claim_lines(self, cr, uid, context):
#TODO use custom states to show buttons of this wizard or not instead of raise an error
if context is None: context = {}
if context is None:
context = {}
line_obj = self.pool.get('claim.line')
if context.get('picking_type') == 'out':
move_field = 'move_out_id'
else:
move_field = 'move_in_id'
good_lines = []
line_ids = line_obj.search(cr, uid,
[('claim_id', '=', context['active_id'])], context=context)
line_ids = line_obj.search(
cr, uid,
[('claim_id', '=', context['active_id'])],
context=context)
for line in line_obj.browse(cr, uid, line_ids, context=context):
if not line[move_field] or line[move_field].state == 'cancel':
good_lines.append(line.id)
if not good_lines:
raise osv.except_osv(_('Error !'),
_('A picking has already been created for this claim !'))
raise orm.except_orm(
_('Error'),
_('A picking has already been created for this claim.'))
return good_lines
# Get default source location
def _get_source_loc(self, cr, uid, context):
loc_id = False
if context is None: context = {}
if context is None:
context = {}
warehouse_obj = self.pool.get('stock.warehouse')
warehouse_id = context.get('warehouse_id')
if context.get('picking_type') == 'out':
loc_id = warehouse_obj.read(cr, uid,
warehouse_id,
loc_id = warehouse_obj.read(
cr, uid, warehouse_id,
['lot_stock_id'],
context=context)['lot_stock_id'][0]
elif context.get('partner_id'):
loc_id = self.pool.get('res.partner').read(cr, uid,
context['partner_id'],
loc_id = self.pool.get('res.partner').read(
cr, uid, context['partner_id'],
['property_stock_customer'],
context=context)['property_stock_customer']
return loc_id
@@ -92,7 +100,7 @@ class claim_make_picking(orm.TransientModel):
for line in line_obj.browse(cr, uid, line_ids, context=context):
if line.location_dest_id.id not in line_location:
line_location.append(line.location_dest_id.id)
if len (line_location) == 1:
if len(line_location) == 1:
loc_id = line_location[0]
return loc_id
@@ -103,33 +111,33 @@ class claim_make_picking(orm.TransientModel):
line_obj = self.pool.get('claim.line')
line_partner = []
for line in line_obj.browse(cr, uid, line_ids, context=context):
if (line.warranty_return_partner and line.warranty_return_partner.id
if (line.warranty_return_partner and line.warranty_return_partner.id
not in line_partner):
line_partner.append(line.warranty_return_partner.id)
if len (line_partner) == 1:
if len(line_partner) == 1:
partner_id = line_partner[0]
return partner_id
# Get default destination location
def _get_dest_loc(self, cr, uid, context):
"""Return the location_id to use as destination.
If it's an outoing shippment: take the customer stock property
If it's an outoing shippment: take the customer stock property
If it's an incomming shippment take the location_dest_id common to all lines, or
if different, return None."""
if context is None: context = {}
warehouse_obj = self.pool.get('stock.warehouse')
warehouse_id = context.get('warehouse_id')
if context is None:
context = {}
loc_id = False
if context.get('picking_type') == 'out' and context.get('partner_id'):
loc_id = self.pool.get('res.partner').read(cr, uid,
context.get('partner_id'),
loc_id = self.pool.get('res.partner').read(
cr, uid, context.get('partner_id'),
['property_stock_customer'],
context=context)['property_stock_customer'][0]
elif context.get('picking_type') == 'in' and context.get('partner_id'):
# Add the case of return to supplier !
# Add the case of return to supplier !
line_ids = self._get_claim_lines(cr, uid, context=context)
loc_id = self._get_common_dest_location_from_line(cr, uid,
line_ids, context=context)
loc_id = self._get_common_dest_location_from_line(cr, uid,
line_ids,
context=context)
return loc_id
_defaults = {
@@ -138,13 +146,14 @@ class claim_make_picking(orm.TransientModel):
'claim_line_ids': _get_claim_lines,
}
def action_cancel(self,cr,uid,ids,conect=None):
return {'type': 'ir.actions.act_window_close',}
def action_cancel(self, cr, uid, ids, context=None):
return {'type': 'ir.actions.act_window_close'}
# If "Create" button pressed
def action_create_picking(self, cr, uid, ids, context=None):
picking_obj = self.pool.get('stock.picking')
if context is None: context = {}
if context is None:
context = {}
view_obj = self.pool.get('ir.ui.view')
name = 'RMA picking out'
if context.get('picking_type') == 'out':
@@ -161,87 +170,96 @@ class claim_make_picking(orm.TransientModel):
if context.get('picking_type'):
note = 'RMA picking ' + str(context.get('picking_type'))
name = note
view_id = view_obj.search(cr, uid, [
('xml_id', '=', view_xml_id),
('model', '=', 'stock.picking'),
('type', '=', 'form'),
('name', '=', view_name)
], context=context)[0]
view_id = view_obj.search(cr, uid,
[('xml_id', '=', view_xml_id),
('model', '=', 'stock.picking'),
('type', '=', 'form'),
('name', '=', view_name)
],
context=context)[0]
wizard = self.browse(cr, uid, ids[0], context=context)
claim = self.pool.get('crm.claim').browse(cr, uid,
context['active_id'], context=context)
claim = self.pool.get('crm.claim').browse(cr, uid,
context['active_id'],
context=context)
partner_id = claim.partner_id.id
line_ids = [x.id for x in wizard.claim_line_ids]
# In case of product return, we don't allow one picking for various
# product if location are different
# or if partner address is different
if context.get('product_return'):
common_dest_loc_id = self._get_common_dest_location_from_line(cr, uid,
line_ids, context=context)
common_dest_loc_id = self._get_common_dest_location_from_line(
cr, uid, line_ids, context=context)
if not common_dest_loc_id:
raise osv.except_osv(_('Error !'),
_('A product return cannot be created for various destination location, please '
'chose line with a same destination location.'))
common_dest_partner_id = self._get_common_partner_from_line(cr, uid,
line_ids, context=context)
raise orm.except_orm(
_('Error !'),
_('A product return cannot be created for various '
'destination locations, please choose line with a '
'same destination location.'))
common_dest_partner_id = self._get_common_partner_from_line(
cr, uid, line_ids, context=context)
if not common_dest_partner_id:
raise osv.except_osv(_('Error !'),
_('A product return cannot be created for various destination address, please '
'chose line with a same address.'))
else:
partner_id = common_dest_partner_id
raise orm.except_orm(
_('Error !'),
_('A product return cannot be created for various '
'destination addresses, please choose line with a '
'same address.'))
partner_id = common_dest_partner_id
# create picking
picking_id = picking_obj.create(cr, uid, {
'origin': claim.number,
'type': p_type,
'move_type': 'one', # direct
'state': 'draft',
'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
'partner_id': partner_id,
'invoice_state': "none",
'company_id': claim.company_id.id,
'location_id': wizard.claim_line_source_location.id,
'location_dest_id': wizard.claim_line_dest_location.id,
'note' : note,
'claim_id': claim.id,
})
picking_id = picking_obj.create(
cr, uid,
{'origin': claim.number,
'type': p_type,
'move_type': 'one', # direct
'state': 'draft',
'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
'partner_id': partner_id,
'invoice_state': "none",
'company_id': claim.company_id.id,
'location_id': wizard.claim_line_source_location.id,
'location_dest_id': wizard.claim_line_dest_location.id,
'note': note,
'claim_id': claim.id,
},
context=context)
# Create picking lines
for wizard_claim_line in wizard.claim_line_ids:
move_id = self.pool.get('stock.move').create(cr, uid, {
'name' : wizard_claim_line.product_id.name_template, # Motif : crm id ? stock_picking_id ?
'priority': '0',
#'create_date':
'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
'date_expected': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
'product_id': wizard_claim_line.product_id.id,
'product_qty': wizard_claim_line.product_returned_quantity,
'product_uom': wizard_claim_line.product_id.uom_id.id,
'partner_id': partner_id,
'prodlot_id': wizard_claim_line.prodlot_id.id,
# 'tracking_id':
'picking_id': picking_id,
'state': 'draft',
'price_unit': wizard_claim_line.unit_sale_price,
# 'price_currency_id': claim_id.company_id.currency_id.id, # from invoice ???
'company_id': claim.company_id.id,
'location_id': wizard.claim_line_source_location.id,
'location_dest_id': wizard.claim_line_dest_location.id,
'note': note,
})
self.pool.get('claim.line').write(cr, uid,
wizard_claim_line.id, {write_field: move_id}, context=context)
move_obj = self.pool.get('stock.move')
move_id = move_obj.create(
cr, uid,
{'name': wizard_claim_line.product_id.name_template,
'priority': '0',
'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
'date_expected': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
'product_id': wizard_claim_line.product_id.id,
'product_qty': wizard_claim_line.product_returned_quantity,
'product_uom': wizard_claim_line.product_id.uom_id.id,
'partner_id': partner_id,
'prodlot_id': wizard_claim_line.prodlot_id.id,
'picking_id': picking_id,
'state': 'draft',
'price_unit': wizard_claim_line.unit_sale_price,
'company_id': claim.company_id.id,
'location_id': wizard.claim_line_source_location.id,
'location_dest_id': wizard.claim_line_dest_location.id,
'note': note,
},
context=context)
self.pool.get('claim.line').write(
cr, uid, wizard_claim_line.id,
{write_field: move_id}, context=context)
wf_service = netsvc.LocalService("workflow")
if picking_id:
wf_service.trg_validate(uid, 'stock.picking',
picking_id,'button_confirm', cr)
wf_service.trg_validate(uid, 'stock.picking',
picking_id, 'button_confirm', cr)
picking_obj.action_assign(cr, uid, [picking_id])
domain = "[('type','=','%s'),('partner_id','=',%s)]"%(p_type, partner_id)
domain = ("[('type', '=', '%s'), ('partner_id', '=', %s)]" %
(p_type, partner_id))
return {
'name': '%s' % name,
'view_type': 'form',
'view_mode': 'form',
'view_id': view_id,
'domain' : domain,
'domain': domain,
'res_model': 'stock.picking',
'res_id': picking_id,
'type': 'ir.actions.act_window',