~camptocamp/c2c-rd-addons/7.0

167.24.2 by office at chricar
[ADD] purchase_landed_costs
1
# -*- coding: utf-8 -*-
2
##############################################################################
3
#
4
#    OpenERP, Open Source Management Solution
5
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
423 by ferdinand
[FIX] Copyright
6
#    Copyright (C) 2010-2012 Camptocamp Austria (<http://www.camptocamp.at>)
167.24.2 by office at chricar
[ADD] purchase_landed_costs
7
#
8
#    This program is free software: you can redistribute it and/or modify
9
#    it under the terms of the GNU Affero General Public License as
10
#    published by the Free Software Foundation, either version 3 of the
11
#    License, or (at your option) any later version.
12
#
13
#    This program is distributed in the hope that it will be useful,
14
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
15
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
#    GNU Affero General Public License for more details.
17
#
18
#    You should have received a copy of the GNU Affero General Public License
19
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
#
21
##############################################################################
22
23
from osv import osv, fields
24
import decimal_precision as dp
25
from tools.translate import _
167.44.37 by Gerhard Könighofer
improved logging, improved import structure
26
import logging
167.24.2 by office at chricar
[ADD] purchase_landed_costs
27
28
class landed_cost_position(osv.osv):
29
    _name = "landed.cost.position"
30
31
    _columns = \
167.24.4 by office at chricar
[FIX] tpyos
32
      { 'product_id' : fields.many2one('product.product','Landed Cost Name', required=True, domain=[('landed_cost_type','!=', False)]),
167.24.2 by office at chricar
[ADD] purchase_landed_costs
33
        'amount'      : fields.float
34
            ( 'Amount'
35
            , required=True
36
            , digits_compute=dp.get_precision('Purchase Price')
37
            , help="""Landed cost for stock valuation. It will be added to the price of the supplier price."""),
38
        'amount_currency': fields.float('Amount Currency', help="The amount expressed in an optional other currency."),
39
        'currency_id': fields.many2one('res.currency', 'Secondary Currency', help="Optional other currency."),
889 by ferdinand
[FIX] make partner mandatory
40
        'partner_id': fields.many2one('res.partner', 'Partner', help="The supplier of this cost component.", required="True"),
167.24.24 by office at chricar
[FIX] text
41
        'price_type': fields.selection( [('per_unit','Per Quantity'), ('value','Absolute Value')], 'Amount Type', required=True,  \
167.24.2 by office at chricar
[ADD] purchase_landed_costs
42
                  help="Defines if the amount is to be calculated for each quantity or an absolute value"),
167.24.4 by office at chricar
[FIX] tpyos
43
        'purchase_order_line_id': fields.many2one('purchase.order.line', 'Purchase Order Line'),
44
        'purchase_order_id': fields.many2one('purchase.order', 'Purchase Order'),
45
        'move_line_id': fields.many2one('stock.move', 'Picking Line'),
46
        'picking_id': fields.many2one('stock.picking', 'Picking'),
167.24.2 by office at chricar
[ADD] purchase_landed_costs
47
      }
48
167.24.23 by office at chricar
[ADD] on chagen trigger
49
    def onchange_product_id(self, cr, uid, ids, product_id, context=None):
50
        if product_id:
51
            prod_obj=self.pool.get('product.product')
52
            prod=prod_obj.browse(cr,uid,[product_id])[0]
53
            v = {'price_type':prod.landed_cost_type}
54
            return {'value': v}
55
        return {}
56
167.24.2 by office at chricar
[ADD] purchase_landed_costs
57
landed_cost_position()
58
59
#----------------------------------------------------------
60
# Purchase Line INHERIT
61
#----------------------------------------------------------
62
class purchase_order_line(osv.osv):
63
    _inherit = "purchase.order.line"
64
65
    def _landing_cost(self, cr, uid, ids, name, args, context):
66
        if not ids : return {}
67
        result = {}
68
        # landed costss for the line
69
        for line in self.browse(cr, uid, ids):
886.1.1 by ferdinand
[REF] landed costs on order (not lines)
70
            landed_costs = 0.0
167.24.2 by office at chricar
[ADD] purchase_landed_costs
71
            if line.landed_cost_line_ids:
72
                for costs in line.landed_cost_line_ids:
73
                    if costs.price_type == 'value':
74
                        landed_costs += costs.amount
75
                    else:       
76
                        landed_costs += costs.amount * line.product_qty
167.24.6 by office at chricar
[FIX] better usability
77
            result[line.id] = landed_costs
78
        return result
79
80
    def _landing_cost_order(self, cr, uid, ids, name, args, context):
81
        if not ids : return {}
82
        result = {}
934.2.2 by David Cormier
Uses distribution method of landed cost instead of product if specified
83
        # landed costs for the line
167.24.6 by office at chricar
[FIX] better usability
84
        for line in self.browse(cr, uid, ids):
886.1.1 by ferdinand
[REF] landed costs on order (not lines)
85
            landed_costs = 0.0
934.2.2 by David Cormier
Uses distribution method of landed cost instead of product if specified
86
            # distribution of landed costs of PO
167.24.2 by office at chricar
[ADD] purchase_landed_costs
87
            if line.order_id.landed_cost_line_ids:
934.2.2 by David Cormier
Uses distribution method of landed cost instead of product if specified
88
                landed_costs += line.order_id.landed_cost_base_value / line.order_id.amount_total * line.price_subtotal + \
167.24.2 by office at chricar
[ADD] purchase_landed_costs
89
                        line.order_id.landed_cost_base_quantity / line.order_id.quantity_total * line.product_qty
90
            result[line.id] = landed_costs
167.24.6 by office at chricar
[FIX] better usability
91
167.24.2 by office at chricar
[ADD] purchase_landed_costs
92
        return result
93
934.2.3 by David Cormier
Adds helper method to calculate percentage of landed cost to be put on po line
94
    def _landing_cost_factor(self, cr, uid, ids, name, args, context):
95
        """
96
        Calculates the percentage of landing costs that should be put on this order line
97
        """
98
        for line in self.browse(cr, uid, ids):
99
            if line.landed_cost_line_ids:
100
                pass
101
167.24.6 by office at chricar
[FIX] better usability
102
167.24.2 by office at chricar
[ADD] purchase_landed_costs
103
    def _landed_cost(self, cr, uid, ids, name, args, context):
104
        if not ids : return {}
105
        result = {}
106
        # landed costss for the line
107
        for line in self.browse(cr, uid, ids):
886.1.1 by ferdinand
[REF] landed costs on order (not lines)
108
            landed_costs = 0.0
167.24.6 by office at chricar
[FIX] better usability
109
            landed_costs += line.price_subtotal + line.landing_costs +  line.landing_costs_order
167.24.2 by office at chricar
[ADD] purchase_landed_costs
110
            result[line.id] = landed_costs
111
112
        return result
113
        
114
    _columns = \
167.24.4 by office at chricar
[FIX] tpyos
115
       {
116
         'landed_cost_line_ids': fields.one2many('landed.cost.position', 'purchase_order_line_id', 'Landed Costs Positions'),
117
         'landing_costs' : fields.function(_landing_cost, digits_compute=dp.get_precision('Account'), string='Landing Costs'),
167.24.6 by office at chricar
[FIX] better usability
118
         'landing_costs_order' : fields.function(_landing_cost_order, digits_compute=dp.get_precision('Account'), string='Landing Costs from Order'),
167.24.4 by office at chricar
[FIX] tpyos
119
         'landed_costs' : fields.function(_landed_cost, digits_compute=dp.get_precision('Account'), string='Landed Costs'),
167.24.2 by office at chricar
[ADD] purchase_landed_costs
120
    }
121
122
purchase_order_line()
123
124
class purchase_order(osv.osv):
125
    _inherit = "purchase.order"
167.47.3 by Gerhard Könighofer
_name-bug fixed
126
    _logger = logging.getLogger(__name__)
167.24.2 by office at chricar
[ADD] purchase_landed_costs
127
128
    def _landed_cost_base_value(self, cr, uid, ids, name, args, context):
129
        if not ids : return {}
130
        result = {}
131
        landed_costs_base_value = 0.0
132
        for line in self.browse(cr, uid, ids):
133
            if line.landed_cost_line_ids:
134
                for costs in line.landed_cost_line_ids:
934.2.2 by David Cormier
Uses distribution method of landed cost instead of product if specified
135
                    if costs.price_type == 'value':
167.24.2 by office at chricar
[ADD] purchase_landed_costs
136
                        landed_costs_base_value += costs.amount
137
            result[line.id] = landed_costs_base_value
138
        return result
139
140
    def _landed_cost_base_quantity(self, cr, uid, ids, name, args, context):
141
        if not ids : return {}
142
        result = {}
143
        landed_costs_base_quantity = 0.0
144
        for line in self.browse(cr, uid, ids):
145
            if line.landed_cost_line_ids:
146
                for costs in line.landed_cost_line_ids:
934.2.2 by David Cormier
Uses distribution method of landed cost instead of product if specified
147
                    if costs.price_type == 'per_unit':
167.24.2 by office at chricar
[ADD] purchase_landed_costs
148
                         landed_costs_base_quantity += costs.amount
149
            result[line.id] = landed_costs_base_quantity
150
        return result
151
152
    def _quantity_total(self, cr, uid, ids, name, args, context):
153
        if not ids : return {}
154
        result = {}
155
        quantity_total = 0.0
156
        for line in self.browse(cr, uid, ids):
157
            if line.order_line:
158
                for pol in line.order_line:
159
                    if pol.product_qty > 0.0:
160
                         quantity_total += pol.product_qty
161
            result[line.id] = quantity_total
162
        return result
163
167.24.4 by office at chricar
[FIX] tpyos
164
    def _landed_cost(self, cr, uid, ids, name, args, context):
165
        if not ids : return {}
166
        result = {}
167
        landed_costs = 0.0
168
        # landed costss for the line
169
        for line in self.browse(cr, uid, ids):
886.1.1 by ferdinand
[REF] landed costs on order (not lines)
170
            landed_costs += line.landing_cost_lines + line.landed_cost_base_value + line.landed_cost_base_quantity + line.amount_untaxed
167.24.4 by office at chricar
[FIX] tpyos
171
            result[line.id] = landed_costs
172
173
        return result
174
167.24.5 by office at chricar
[FIX] calculation landed cost
175
    def _landing_cost_lines(self, cr, uid, ids, name, args, context):
176
        if not ids : return {}
177
        result = {}
178
        landed_cost_lines = 0.0
179
        for line in self.browse(cr, uid, ids):
180
            if line.order_line:
181
                for pol in line.order_line:
182
                    if pol.product_qty > 0.0:
183
                         landed_cost_lines += pol.landing_costs
184
            result[line.id] = landed_cost_lines
185
        return result
186
167.24.2 by office at chricar
[ADD] purchase_landed_costs
187
188
    _columns = \
167.24.4 by office at chricar
[FIX] tpyos
189
        {
190
         'landed_cost_line_ids': fields.one2many('landed.cost.position', 'purchase_order_id', 'Landed Costs'),
167.24.2 by office at chricar
[ADD] purchase_landed_costs
191
         'landed_cost_base_value' : fields.function(_landed_cost_base_value, digits_compute=dp.get_precision('Account'), string='Landed Costs Base Value'),
192
         'landed_cost_base_quantity' : fields.function(_landed_cost_base_quantity, digits_compute=dp.get_precision('Account'), string='Landed Costs Base Quantity'),
167.24.5 by office at chricar
[FIX] calculation landed cost
193
         'landing_cost_lines' : fields.function(_landing_cost_lines, digits_compute=dp.get_precision('Account'), string='Landing Cost Lines'),
194
         'landed_cost' : fields.function(_landed_cost, digits_compute=dp.get_precision('Account'), string='Landed Costs Total Untaxed'),
167.24.2 by office at chricar
[ADD] purchase_landed_costs
195
         'quantity_total' : fields.function(_quantity_total, digits_compute=dp.get_precision('Product UoM'), string='Total Quantity'),
196
    }
197
198
    def _prepare_order_line_move(self, cr, uid, order, order_line, picking_id, context=None):
199
        res = super(purchase_order,self)._prepare_order_line_move( cr, uid, order, order_line, picking_id, context)
167.24.17 by office at chricar
[FIX] calculation landed cost
200
        res['price_unit_net'] =  res['price_unit']
167.24.13 by office at chricar
[FIX] correct value for stock move
201
        res['price_unit'] = order_line.landed_costs / order_line.product_qty        
167.24.8 by office at chricar
[FREF] landing costs for pick
202
        return res
203
204
    def _prepare_order_picking(self, cr, uid, order, context=None):
205
        res = super(purchase_order,self)._prepare_order_picking( cr, uid, order, context)
167.24.9 by office at chricar
[REF] landing costs for pick copied
206
207
        return res
208
934.2.3 by David Cormier
Adds helper method to calculate percentage of landed cost to be put on po line
209
    def _get_product_account_expense_id(self, product):
210
        """
211
        Returns the product's account expense id if present
212
        or it's parent categories account expense id otherwise
213
        """
214
        if product.property_account_expense.id:
215
            return product.property_account_expense.id
216
        return product.categ_id.property_account_expense_categ.id
217
            
218
167.24.9 by office at chricar
[REF] landing costs for pick copied
219
    def _create_pickings(self, cr, uid, order, order_lines, picking_id=False, context=None): 
220
        res =  super(purchase_order,self)._create_pickings(cr, uid, order, order_lines, picking_id, context)
221
        pick_id = int(res[0])
886.1.2 by ferdinand
[REF] landed cost invoices
222
        # landing costs Invoices from PO 
223
        #cost_obj = self.pool.get('landed.cost.position')
224
        invoice_obj = self.pool.get('account.invoice')
225
        invoice_line_obj = self.pool.get('account.invoice.line')
226
        journal_obj = self.pool.get('account.journal')
227
        journal_ids = journal_obj.search(cr, uid, [('type', '=','purchase'),('company_id', '=', order.company_id.id)], limit=1)
167.24.9 by office at chricar
[REF] landing costs for pick copied
228
        for order_cost in order.landed_cost_line_ids:
886.1.2 by ferdinand
[REF] landed cost invoices
229
            vals_inv = {
230
            'partner_id' : order_cost.partner_id.id
888 by ferdinand
[FIX] missing fields in form
231
           #,'amount' : order_cost.amount
232
           #,'amount_currency' : order_cost.amount_currency
886.1.2 by ferdinand
[REF] landed cost invoices
233
           ,'currency_id' : order_cost.currency_id.id or order.company_id.currency_id.id
234
           ,'account_id' : order_cost.partner_id.property_account_payable.id
235
           ,'type' : 'in_invoice'
236
           ,'origin' : order.name
237
           ,'fiscal_position':  order.partner_id.property_account_position and order.partner_id.property_account_position.id or False
238
           ,'company_id': order.company_id.id
239
           ,'journal_id': len(journal_ids) and journal_ids[0] or False
240
241
                }
242
            self._logger.debug('vals inv`%s`', vals_inv)
243
            #cost_obj.create(cr, uid, vals, context=None) 
244
            inv_id = invoice_obj.create(cr, uid, vals_inv, context=None) 
245
            vals_line = {
246
            'product_id' : order_cost.product_id.id
247
           ,'name' : order_cost.product_id.name
888 by ferdinand
[FIX] missing fields in form
248
           #,'amount' : order_cost.amount
249
           #,'amount_currency' : order_cost.amount_currency
250
           #,'picking_id' : pick_id
934.2.3 by David Cormier
Adds helper method to calculate percentage of landed cost to be put on po line
251
           ,'account_id' : self._get_product_account_expense_id(order_cost.product_id)
886.1.2 by ferdinand
[REF] landed cost invoices
252
           ,'partner_id' : order_cost.partner_id.id
253
           ,'invoice_id' : inv_id
254
           ,'price_unit' : order_cost.amount
255
           ,'invoice_line_tax_id': [(6, 0, [x.id for x in order_cost.product_id.supplier_taxes_id])],
256
257
                }
258
            self._logger.debug('vals line `%s`', vals_line)
259
            inv_line_id = invoice_line_obj.create(cr, uid, vals_line, context=None) 
260
            
167.24.9 by office at chricar
[REF] landing costs for pick copied
261
262
        #self.pool.get('landed.cost.position').create(cr, uid, cost_lines, context=None) 
263
        # landing costs for PICK Lines from PO   
886.1.2 by ferdinand
[REF] landed cost invoices
264
        #pick_obj = self.pool.get('stock.picking')
265
        #for pick in pick_obj.browse(cr, uid, [pick_id], context=None):
266
        #  self._logger.debug('pick `%s`', pick)
267
        #  for line in pick.move_lines:
268
        #   self._logger.debug('line `%s`', line)
269
        #   for order_cost in line.purchase_line_id.landed_cost_line_ids:
270
        #    vals = {}
271
        #    vals['product_id'] = order_cost.product_id.id
272
        #    vals['partner_id'] = order_cost.partner_id.id
273
        #    vals['amount'] = order_cost.amount
274
        #    vals['amount_currency'] = order_cost.amount_currency
275
        #    vals['currency_id'] = order_cost.currency_id.id
276
        #    vals['price_type'] = order_cost.price_type
277
        #    vals['move_line_id'] = line.id
278
        #    self._logger.debug('vals `%s`', vals)
279
        #    cost_obj.create(cr, uid, vals, context=None) 
280
        #self._logger.debug('cost created')
167.24.9 by office at chricar
[REF] landing costs for pick copied
281
           
282
        return res
167.24.2 by office at chricar
[ADD] purchase_landed_costs
283
284
purchase_order()