1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# Copyright (C) 2012 Agile Business Group sagl (<http://www.agilebg.com>)
5
# Copyright (C) 2012 Domsense srl (<http://www.domsense.com>)
7
# This program is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU Affero General Public License as published
9
# by the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
17
# You should have received a copy of the GNU Affero General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
20
##############################################################################
22
from openerp.osv import fields, orm
23
from openerp.tools.translate import _
24
import openerp.addons.decimal_precision as dp
26
class sale_delivery_term(orm.Model):
27
_name = 'sale.delivery.term'
29
'name': fields.char('Name', size=64, required=True),
30
'line_ids': fields.one2many('sale.delivery.term.line', 'term_id', 'Lines', required=True),
31
'company_id': fields.many2one('res.company','Company',required=True,select=1),
34
'company_id': lambda self,cr,uid,c: self.pool.get(
35
'res.company')._company_default_get(cr, uid, 'sale.delivery.term', context=c),
38
def is_total_percentage_correct(self, cr, uid, term_ids, context=None):
39
for term in self.browse(cr, uid, term_ids, context=context):
41
for line in term.line_ids:
42
total += line.quantity_perc
47
class sale_delivery_term_line(orm.Model):
49
_name = 'sale.delivery.term.line'
52
'term_id': fields.many2one('sale.delivery.term', 'Term'),
53
'quantity_perc': fields.float('Quantity percentage', required=True, help="For 20% set '0.2'"),
54
'delay': fields.float('Delivery Lead Time', required=True,
55
help="Number of days between the order confirmation and the shipping of the products to the customer"),
58
class sale_order_line_master(orm.Model):
60
def _clean_on_change_dict(self, res_dict):
61
if res_dict['value'].has_key('delay'):
62
del res_dict['value']['delay']
63
if res_dict['value'].has_key('th_weight'):
64
del res_dict['value']['th_weight']
65
if res_dict['value'].has_key('type'):
66
del res_dict['value']['type']
67
if res_dict['value'].has_key('tax_id'):
68
del res_dict['value']['tax_id']
71
def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
72
uom=False, qty_uos=0, uos=False, name='', partner_id=False,
73
lang=False, update_tax=True, date_order=False, packaging=False,
74
fiscal_position=False, flag=False, context=None):
75
res = self.pool.get('sale.order.line').product_id_change(cr, uid, ids, pricelist, product, qty=qty,
76
uom=uom, qty_uos=qty_uos, uos=uos, name=name, partner_id=partner_id,
77
lang=lang, update_tax=update_tax, date_order=date_order,
78
packaging=packaging, fiscal_position=fiscal_position, flag=flag, context=context)
79
return self._clean_on_change_dict(res)
81
def product_uom_change(self, cursor, user, ids, pricelist, product, qty=0,
82
uom=False, qty_uos=0, uos=False, name='', partner_id=False,
83
lang=False, update_tax=True, date_order=False, context=None):
84
res = self.pool.get('sale.order.line').product_uom_change(cursor, user, ids, pricelist, product, qty=qty,
85
uom=uom, qty_uos=qty_uos, uos=uos, name=name, partner_id=partner_id,
86
lang=lang, update_tax=update_tax, date_order=date_order, context=context)
87
return self._clean_on_change_dict(res)
89
def product_packaging_change(self, cr, uid, ids, pricelist, product, qty=0, uom=False,
90
partner_id=False, packaging=False, flag=False, context=None):
91
return self.pool.get('sale.order.line').product_packaging_change(
92
cr, uid, ids, pricelist, product, qty=qty, uom=uom,
93
partner_id=partner_id, packaging=packaging, flag=flag, context=context)
95
def _amount_line(self, cr, uid, ids, field_name, arg, context=None):
96
tax_obj = self.pool.get('account.tax')
97
cur_obj = self.pool.get('res.currency')
101
for line in self.browse(cr, uid, ids, context=context):
102
price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
103
taxes = tax_obj.compute_all(cr, uid, line.tax_ids, price,
104
line.product_uom_qty, line.order_id.partner_invoice_id.id,
105
line.product_id, line.order_id.partner_id)
106
cur = line.order_id.pricelist_id.currency_id
107
res[line.id] = cur_obj.round(cr, uid, cur, taxes['total'])
110
def _get_uom_id(self, cr, uid, *args):
111
return self.pool.get('sale.order.line')._get_uom_id(cr, uid, args)
113
_name = 'sale.order.line.master'
115
'order_id': fields.many2one('sale.order', 'Order Reference', required=True, ondelete='cascade'),
116
'delivery_term_id': fields.many2one('sale.delivery.term', 'Delivery term', required=True),
117
'name': fields.char('Description', size=256, required=True),
118
'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)]),
119
'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Sale Price')),
120
'price_subtotal': fields.function(_amount_line, string='Subtotal',
121
digits_compute= dp.get_precision('Sale Price')),
122
'product_uom_qty': fields.float('Quantity (UoM)', digits_compute= dp.get_precision('Product UoS'), required=True),
123
'product_uom': fields.many2one('product.uom', 'Unit of Measure ', required=True),
124
'product_uos_qty': fields.float('Quantity (UoS)' ,digits_compute= dp.get_precision('Product UoS')),
125
'product_uos': fields.many2one('product.uom', 'Product UoS'),
126
'product_packaging': fields.many2one('product.packaging', 'Packaging'),
127
'order_line_ids': fields.one2many('sale.order.line', 'master_line_id', 'Detailed lines'),
128
'discount': fields.float('Discount (%)', digits=(16, 2)),
129
'tax_ids': fields.many2many('account.tax', 'sale_master_order_line_tax', 'order_line_id', 'tax_id', 'Taxes'),
132
'product_uom' : _get_uom_id,
133
'product_uom_qty': 1,
134
'product_uos_qty': 1,
135
'product_packaging': False,
139
def _prepare_order_line(self, cr, uid, term_line, master_line, group_index=0, context=None):
140
order_line_pool = self.pool.get('sale.order.line')
141
group_pool = self.pool.get('sale.order.line.group')
142
group_ids = group_pool.search(cr, uid, [])
143
product_uom_qty = master_line.product_uom_qty * term_line.quantity_perc
144
product_uos_qty = master_line.product_uos_qty * term_line.quantity_perc
146
on_change_res = order_line_pool.product_id_change(cr, uid, [], master_line.order_id.pricelist_id.id,
147
master_line.product_id.id, qty=product_uom_qty,
148
uom=master_line.product_uom.id, qty_uos=product_uos_qty,
149
uos=master_line.product_uos.id, name=master_line.name, partner_id=master_line.order_id.partner_id.id,
150
lang=False, update_tax=True, date_order=master_line.order_id.date_order,
151
packaging=master_line.product_packaging.id, fiscal_position=master_line.order_id.fiscal_position.id,
152
flag=False, context=context)
153
order_line_vals.update(on_change_res['value'])
154
order_line_vals.update({
155
'order_id': master_line.order_id.id,
156
'name': master_line.name,
157
'price_unit': master_line.price_unit,
158
'product_uom_qty': product_uom_qty,
159
'product_uom': master_line.product_uom.id,
160
'product_id': master_line.product_id and master_line.product_id.id or False,
161
'product_uos_qty': product_uos_qty,
162
'product_uos': master_line.product_uos and master_line.product_uos.id or False,
163
'product_packaging': master_line.product_packaging.id,
164
'master_line_id': master_line.id,
165
'delay': term_line.delay,
166
'picking_group_id': group_ids[group_index],
167
'tax_id': [(6,0, [tax.id for tax in master_line.tax_ids])],
169
return order_line_vals
172
def generate_detailed_lines(self, cr, uid, ids, context=None):
173
group_pool = self.pool.get('sale.order.line.group')
174
order_line_pool = self.pool.get('sale.order.line')
175
group_ids = group_pool.search(cr, uid, [])
176
for master_line in self.browse(cr, uid, ids):
177
if master_line.order_line_ids:
178
raise orm.except_orm(_('Error'),
179
_("Detailed lines generated yet (for master line '%s'). Remove them first") % master_line.name)
180
if len(master_line.delivery_term_id.line_ids) > len(group_ids):
181
raise orm.except_orm(_('Error'),
182
_("Delivery term lines are %d. Order line groups are %d. Please create more groups")
183
% (len(master_line.delivery_term_id.line_ids), len(group_ids)))
184
if not master_line.delivery_term_id.is_total_percentage_correct():
185
raise orm.except_orm(_('Error'),
186
_("Total percentage of delivery term %s is not equal to 1") % master_line.delivery_term_id.name)
187
for group_index, term_line in enumerate(master_line.delivery_term_id.line_ids):
188
order_line_vals = self._prepare_order_line(
189
cr, uid, term_line, master_line, group_index=group_index, context=context)
190
order_line_pool.create(cr, uid, order_line_vals, context=context)
193
def copy_data(self, cr, uid, id, default=None, context=None):
197
'order_line_ids': [],
199
return super(sale_order_line_master, self).copy_data(cr, uid, id, default, context=context)
202
class sale_order_line(orm.Model):
203
_inherit = 'sale.order.line'
205
'master_line_id': fields.many2one('sale.order.line.master', 'Master Line'),
208
def copy_data(self, cr, uid, id, default=None, context=None):
211
default.update({'master_line_id': False})
212
return super(sale_order_line, self).copy_data(cr, uid, id, default, context=context)
215
class sale_order(orm.Model):
216
_inherit = 'sale.order'
218
'master_order_line': fields.one2many('sale.order.line.master', 'order_id', 'Master Order Lines', readonly=True, states={'draft': [('readonly', False)]}),
221
def copy(self, cr, uid, id, default=None, context=None):
227
return super(sale_order, self).copy(cr, uid, id, default, context=context)
229
def generate_detailed_lines(self, cr, uid, ids, context=None):
230
for order in self.browse(cr, uid, ids, context):
231
for master_line in order.master_order_line:
232
master_line.generate_detailed_lines()