1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
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.
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.
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 operator import itemgetter
23 from osv import fields, osv
26 class account_fiscal_position(osv.osv):
27 _name = 'account.fiscal.position'
28 _description = 'Fiscal Position'
30 'name': fields.char('Fiscal Position', size=64, required=True),
31 'active': fields.boolean('Active', help="By unchecking the active field, you may hide a fiscal position without deleting it."),
32 'company_id': fields.many2one('res.company', 'Company'),
33 'account_ids': fields.one2many('account.fiscal.position.account', 'position_id', 'Account Mapping'),
34 'tax_ids': fields.one2many('account.fiscal.position.tax', 'position_id', 'Tax Mapping'),
35 'note': fields.text('Notes', translate=True),
42 def map_tax(self, cr, uid, fposition_id, taxes, context=None):
46 return map(lambda x: x.id, taxes)
50 for tax in fposition_id.tax_ids:
51 if tax.tax_src_id.id == t.id:
53 result.add(tax.tax_dest_id.id)
59 def map_account(self, cr, uid, fposition_id, account_id, context=None):
62 for pos in fposition_id.account_ids:
63 if pos.account_src_id.id == account_id:
64 account_id = pos.account_dest_id.id
68 account_fiscal_position()
70 class account_fiscal_position_tax(osv.osv):
71 _name = 'account.fiscal.position.tax'
72 _description = 'Taxes Fiscal Position'
73 _rec_name = 'position_id'
75 'position_id': fields.many2one('account.fiscal.position', 'Fiscal Position', required=True, ondelete='cascade'),
76 'tax_src_id': fields.many2one('account.tax', 'Tax Source', required=True),
77 'tax_dest_id': fields.many2one('account.tax', 'Replacement Tax')
82 'unique (position_id,tax_src_id,tax_dest_id)',
83 'A tax fiscal position could be defined only once time on same taxes.')
86 account_fiscal_position_tax()
88 class account_fiscal_position_account(osv.osv):
89 _name = 'account.fiscal.position.account'
90 _description = 'Accounts Fiscal Position'
91 _rec_name = 'position_id'
93 'position_id': fields.many2one('account.fiscal.position', 'Fiscal Position', required=True, ondelete='cascade'),
94 'account_src_id': fields.many2one('account.account', 'Account Source', domain=[('type','<>','view')], required=True),
95 'account_dest_id': fields.many2one('account.account', 'Account Destination', domain=[('type','<>','view')], required=True)
99 ('account_src_dest_uniq',
100 'unique (position_id,account_src_id,account_dest_id)',
101 'An account fiscal position could be defined only once time on same accounts.')
104 account_fiscal_position_account()
106 class res_partner(osv.osv):
107 _name = 'res.partner'
108 _inherit = 'res.partner'
109 _description = 'Partner'
111 def _credit_debit_get(self, cr, uid, ids, field_names, arg, context=None):
112 query = self.pool.get('account.move.line')._query_get(cr, uid, context=context)
113 cr.execute("""SELECT l.partner_id, a.type, SUM(l.debit-l.credit)
114 FROM account_move_line l
115 LEFT JOIN account_account a ON (l.account_id=a.id)
116 WHERE a.type IN ('receivable','payable')
117 AND l.partner_id IN %s
118 AND l.reconcile_id IS NULL
119 AND """ + query + """
120 GROUP BY l.partner_id, a.type
123 maps = {'receivable':'credit', 'payable':'debit' }
126 res[id] = {}.fromkeys(field_names, 0)
127 for pid,type,val in cr.fetchall():
128 if val is None: val=0
129 res[pid][maps[type]] = (type=='receivable') and val or -val
132 def _asset_difference_search(self, cr, uid, obj, name, type, args, context=None):
135 having_values = tuple(map(itemgetter(2), args))
136 where = ' AND '.join(
137 map(lambda x: '(SUM(debit-credit) %(operator)s %%s)' % {
138 'operator':x[1]},args))
139 query = self.pool.get('account.move.line')._query_get(cr, uid, context=context)
140 cr.execute(('SELECT partner_id FROM account_move_line l '\
141 'WHERE account_id IN '\
142 '(SELECT id FROM account_account '\
143 'WHERE type=%s AND active) '\
144 'AND reconcile_id IS NULL '\
146 'AND partner_id IS NOT NULL '\
147 'GROUP BY partner_id HAVING '+where),
148 (type,) + having_values)
151 return [('id','=','0')]
152 return [('id','in',map(itemgetter(0), res))]
154 def _credit_search(self, cr, uid, obj, name, args, context=None):
155 return self._asset_difference_search(cr, uid, obj, name, 'receivable', args, context=context)
157 def _debit_search(self, cr, uid, obj, name, args, context=None):
158 return self._asset_difference_search(cr, uid, obj, name, 'payable', args, context=context)
160 def has_something_to_reconcile(self, cr, uid, partner_id, context=None):
162 at least a debit, a credit and a line older than the last reconciliation date of the partner
165 SELECT l.partner_id, SUM(l.debit) AS debit, SUM(l.credit) AS credit
166 FROM account_move_line l
167 RIGHT JOIN account_account a ON (a.id = l.account_id)
168 RIGHT JOIN res_partner p ON (l.partner_id = p.id)
169 WHERE a.reconcile IS TRUE
171 AND l.reconcile_id IS NULL
172 AND (p.last_reconciliation_date IS NULL OR l.date > p.last_reconciliation_date)
173 AND l.state <> 'draft'
174 GROUP BY l.partner_id''', (partner_id,))
175 res = cr.dictfetchone()
177 return bool(res['debit'] and res['credit'])
180 def mark_as_reconciled(self, cr, uid, ids, context=None):
181 return self.write(cr, uid, ids, {'last_reconciliation_date': time.strftime('%Y-%m-%d %H:%M:%S')}, context=context)
184 'credit': fields.function(_credit_debit_get,
185 fnct_search=_credit_search, string='Total Receivable', multi='dc', help="Total amount this customer owes you."),
186 'debit': fields.function(_credit_debit_get, fnct_search=_debit_search, string='Total Payable', multi='dc', help="Total amount you have to pay to this supplier."),
187 'debit_limit': fields.float('Payable Limit'),
188 'property_account_payable': fields.property(
191 relation='account.account',
192 string="Account Payable",
194 domain="[('type', '=', 'payable')]",
195 help="This account will be used instead of the default one as the payable account for the current partner",
197 'property_account_receivable': fields.property(
200 relation='account.account',
201 string="Account Receivable",
203 domain="[('type', '=', 'receivable')]",
204 help="This account will be used instead of the default one as the receivable account for the current partner",
206 'property_account_position': fields.property(
207 'account.fiscal.position',
209 relation='account.fiscal.position',
210 string="Fiscal Position",
212 help="The fiscal position will determine taxes and accounts used for the partner.",
214 'property_payment_term': fields.property(
215 'account.payment.term',
217 relation='account.payment.term',
218 string ='Payment Term',
220 help="This payment term will be used instead of the default one for the current partner"),
221 'ref_companies': fields.one2many('res.company', 'partner_id',
222 'Companies that refers to partner'),
223 'last_reconciliation_date': fields.datetime('Latest Reconciliation Date', help='Date on which the partner accounting entries were fully reconciled last time. It differs from the date of the last reconciliation made for this partner, as here we depict the fact that nothing more was to be reconciled at this date. This can be achieved in 2 ways: either the last debit/credit entry was reconciled, either the user pressed the button "Fully Reconciled" in the manual reconciliation process')
228 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: