[MERGE] Merge from trunk-wms-loconopreport-jco
[odoo/odoo.git] / addons / stock_account / stock.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
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
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
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 Affero General Public License for more details.
16 #
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/>.
19 #
20 ##############################################################################
21
22 from openerp.osv import fields, osv
23
24 #----------------------------------------------------------
25 # Procurement Rule
26 #----------------------------------------------------------
27 class procurement_rule(osv.osv):
28     _inherit = 'procurement.rule'
29     _columns = {
30         'invoice_state': fields.selection([
31             ("invoiced", "Invoiced"),
32             ("2binvoiced", "To Be Invoiced"),
33             ("none", "Not Applicable")], "Invoice Status",
34             required=False),
35         }
36
37 #----------------------------------------------------------
38 # Procurement Order
39 #----------------------------------------------------------
40
41
42 class procurement_order(osv.osv):
43     _inherit = "procurement.order"
44     _columns = {
45         'invoice_state': fields.selection([("invoiced", "Invoiced"),
46             ("2binvoiced", "To Be Invoiced"),
47             ("none", "Not Applicable")
48          ], "Invoice Control", required=True),
49         }
50
51     def _run_move_create(self, cr, uid, procurement, context=None):
52         res = super(procurement_order, self)._run_move_create(cr, uid, procurement, context=context)
53         res.update({'invoice_state': (procurement.rule_id.invoice_state in ('none', False) and procurement.invoice_state or procurement.rule_id.invoice_state) or 'none'})
54         return res
55
56     _defaults = {
57         'invoice_state': 'none'
58         }
59
60
61 #----------------------------------------------------------
62 # Move
63 #----------------------------------------------------------
64
65 class stock_move(osv.osv):
66     _inherit = "stock.move"
67     _columns = {
68         'invoice_state': fields.selection([("invoiced", "Invoiced"),
69             ("2binvoiced", "To Be Invoiced"),
70             ("none", "Not Applicable")], "Invoice Control",
71             select=True, required=True, track_visibility='onchange',
72             states={'draft': [('readonly', False)]}),
73         }
74     _defaults = {
75         'invoice_state': lambda *args, **argv: 'none'
76     }
77
78     def _get_master_data(self, cr, uid, move, company, context=None):
79         ''' returns a tupple (browse_record(res.partner), ID(res.users), ID(res.currency)'''
80         return move.picking_id.partner_id, uid, company.currency_id.id
81
82     def _create_invoice_line_from_vals(self, cr, uid, move, invoice_line_vals, context=None):
83         return self.pool.get('account.invoice.line').create(cr, uid, invoice_line_vals, context=context)
84
85     def _get_invoice_line_vals(self, cr, uid, move, partner, inv_type, context=None):
86         fp_obj = self.pool.get('account.fiscal.position')
87         # Get account_id
88         if inv_type in ('out_invoice', 'out_refund'):
89             account_id = move.product_id.property_account_income.id
90             if not account_id:
91                 account_id = move.product_id.categ_id.property_account_income_categ.id
92         else:
93             account_id = move.product_id.property_account_expense.id
94             if not account_id:
95                 account_id = move.product_id.categ_id.property_account_expense_categ.id
96         fiscal_position = partner.property_account_position
97         account_id = fp_obj.map_account(cr, uid, fiscal_position, account_id)
98
99         # set UoS if it's a sale and the picking doesn't have one
100         uos_id = move.product_uom.id
101         quantity = move.product_uom_qty
102         if move.product_uos:
103             uos_id = move.product_uos.id
104             quantity = move.product_uos_qty
105         return {
106             'name': move.name,
107             'account_id': account_id,
108             'product_id': move.product_id.id,
109             'uos_id': uos_id,
110             'quantity': quantity,
111             'price_unit': move.product_id.list_price,  # TODO: use price_get
112             'discount': 0.0,
113             'account_analytic_id': False,
114         }
115
116 #----------------------------------------------------------
117 # Picking
118 #----------------------------------------------------------
119
120 class stock_picking(osv.osv):
121     _inherit = 'stock.picking'
122     def __get_invoice_state(self, cr, uid, ids, name, arg, context=None):
123         result = {}
124         for pick in self.browse(cr, uid, ids, context=context):
125             result[pick.id] = 'none'
126             for move in pick.move_lines:
127                 if move.invoice_state == 'invoiced':
128                     result[pick.id] = 'invoiced'
129                 elif move.invoice_state == '2binvoiced':
130                     result[pick.id] = '2binvoiced'
131                     break
132         return result
133
134     def __get_picking_move(self, cr, uid, ids, context={}):
135         res = []
136         for move in self.pool.get('stock.move').browse(cr, uid, ids, context=context):
137             if move.picking_id:
138                 res.append(move.picking_id.id)
139         return res
140
141     _columns = {
142         'invoice_state': fields.function(__get_invoice_state, type='selection', selection=[
143             ("invoiced", "Invoiced"),
144             ("2binvoiced", "To Be Invoiced"),
145             ("none", "Not Applicable")
146           ], string="Invoice Control", required=True,
147
148         store={
149             'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['state'], 10),
150             'stock.move': (__get_picking_move, ['picking_id', 'invoice_state'], 10),
151         },
152         ),
153     }
154     _defaults = {
155         'invoice_state': lambda *args, **argv: 'none'
156     }
157
158     def _create_invoice_from_picking(self, cr, uid, picking, vals, context=None):
159         ''' This function simply creates the invoice from the given values. It is overriden in delivery module to add the delivery costs.
160         '''
161         invoice_obj = self.pool.get('account.invoice')
162         return invoice_obj.create(cr, uid, vals, context=context)
163
164     def action_invoice_create(self, cr, uid, ids, journal_id=False, group=False, type='out_invoice', context=None):
165         """ Creates invoice based on the invoice state selected for picking.
166         @param journal_id: Id of journal
167         @param group: Whether to create a group invoice or not
168         @param type: Type invoice to be created
169         @return: Ids of created invoices for the pickings
170         """
171         context = context or {}
172         todo = {}
173         for picking in self.browse(cr, uid, ids, context=context):
174             key = group and picking.id or True
175             for move in picking.move_lines:
176                 if move.procurement_id and (move.procurement_id.invoice_state == '2binvoiced') or move.invoice_state == '2binvoiced':
177                     if (move.state != 'cancel') and not move.scrapped:
178                         todo.setdefault(key, [])
179                         todo[key].append(move)
180         invoices = []
181         for moves in todo.values():
182             invoices = self.__invoice_create_line(cr, uid, moves, journal_id, type, context=context)
183         return invoices
184
185     def __invoice_create_line(self, cr, uid, moves, journal_id=False, inv_type='out_invoice', context=None):
186         invoice_obj = self.pool.get('account.invoice')
187         move_obj = self.pool.get('stock.move')
188         invoices = {}
189         for move in moves:
190             company = move.company_id
191             origin = move.picking_id.name
192             partner, user_id, currency_id = move_obj._get_master_data(cr, uid, move, company, context=context)
193
194             key = (partner.id, currency_id, company.id, user_id)
195
196             if key not in invoices:
197                 # Get account and payment terms
198                 if inv_type in ('out_invoice', 'out_refund'):
199                     account_id = partner.property_account_receivable.id
200                     payment_term = partner.property_payment_term.id or False
201                 else:
202                     account_id = partner.property_account_payable.id
203                     payment_term = partner.property_supplier_payment_term.id or False
204
205                 invoice_vals = {
206                     'origin': origin,
207                     'date_invoice': context.get('date_inv', False),
208                     'user_id': user_id,
209                     'partner_id': partner.id,
210                     'account_id': account_id,
211                     'payment_term': payment_term,
212                     'type': inv_type,
213                     'fiscal_position': partner.property_account_position.id,
214                     'company_id': company.id,
215                     'currency_id': currency_id,
216                     'journal_id': journal_id,
217                 }
218                 invoice_id = self._create_invoice_from_picking(cr, uid, move.picking_id, invoice_vals, context=context)
219                 invoices[key] = invoice_id
220
221             invoice_line_vals = move_obj._get_invoice_line_vals(cr, uid, move, partner, inv_type, context=context)
222             invoice_line_vals['invoice_id'] = invoices[key]
223             invoice_line_vals['origin'] = origin
224
225             move_obj._create_invoice_line_from_vals(cr, uid, move, invoice_line_vals, context=context)
226
227             move_obj.write(cr, uid, move.id, {'invoice_state': 'invoiced'}, context=context)
228             if move.procurement_id:
229                 self.pool.get('procurement.order').write(cr, uid, [move.procurement_id.id], {
230                     'invoice_state': 'invoiced',
231                 }, context=context)
232
233         invoice_obj.button_compute(cr, uid, invoices.values(), context=context, set_total=(inv_type in ('in_invoice', 'in_refund')))
234         return invoices.values()