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 ##############################################################################
21 from mx import DateTime
22 from osv import fields, osv, orm
23 from tools import config
24 from tools.translate import _
31 #----------------------------------------------------------
33 #----------------------------------------------------------
35 class auction_artists(osv.osv):
36 _name = "auction.artists"
38 'name': fields.char('Artist/Author Name', size=64, required=True),
39 'pseudo': fields.char('Pseudo', size=64),
40 'birth_death_dates':fields.char('Birth / Death dates', size=64),
41 'biography': fields.text('Biography'),
45 #----------------------------------------------------------
47 #----------------------------------------------------------
48 class auction_dates(osv.osv):
50 _name = "auction.dates"
53 def _adjudication_get(self, cr, uid, ids, prop, unknow_none, unknow_dict):
56 lots_obj = self.pool.get('auction.lots')
57 for auction in self.browse(cr, uid, ids):
58 lots_ids = lots_obj.search(cr, uid, [('auction_id', '=', auction.id)])
59 for lots in lots_obj.browse(cr, uid, lots_ids):
60 total+=lots.obj_price or 0.0
64 def name_get(self, cr, uid, ids, context=None):
69 reads = self.read(cr, uid, ids, ['name', 'auction1'], context)
70 name = [(r['id'], '['+r['auction1']+'] '+ r['name']) for r in reads]
74 'name': fields.char('Auction Name', size=64, required=True),
75 'expo1': fields.date('First Exposition Day', required=True, help="Beginning Exposition Date For Auction"),
76 'expo2': fields.date('Last Exposition Day', required=True, help="Last Exposition Date For Auction"),
77 'auction1': fields.date('First Auction Day', required=True, help="Start Date Of Auction"),
78 'auction2': fields.date('Last Auction Day', required=True, help="End Date Of Auction"),
79 'journal_id': fields.many2one('account.journal', 'Buyer Journal', required=True, help="Account Journal For Buyer"),
80 'journal_seller_id': fields.many2one('account.journal', 'Seller Journal', required=True, help="Account Journal For Seller"),
81 'buyer_costs': fields.many2many('account.tax', 'auction_buyer_taxes_rel', 'auction_id', 'tax_id', 'Buyer Costs', help="Account Tax For Buyer"),
82 'seller_costs': fields.many2many('account.tax', 'auction_seller_taxes_rel', 'auction_id', 'tax_id', 'Seller Costs', help="Account Tax For Seller"),
83 'acc_income': fields.many2one('account.account', 'Income Account', required=True),
84 'acc_expense': fields.many2one('account.account', 'Expense Account', required=True),
85 'adj_total': fields.function(_adjudication_get, method=True, string='Total Adjudication', store=True),
86 'state': fields.selection((('draft', 'Draft'), ('closed', 'Closed')), 'State', select=1, readonly=True,
87 help='When auction starts the state is \'Draft\'.\n At the end of auction, the state becomes \'Closed\'.'),
88 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account', required=True),
92 'state': lambda *a: 'draft',
95 _order = "auction1 desc"
97 def close(self, cr, uid, ids, context=None):
99 Close an auction date.
101 Create invoices for all buyers and sellers.
108 lots_obj = self.pool.get('auction.lots')
109 lots_ids = lots_obj.search(cr, uid, [('auction_id', 'in', ids), ('state', '=', 'draft'), ('obj_price', '>', 0)])
110 new_buyer_invoice = lots_obj.lots_invoice(cr, uid, lots_ids, {}, None)
111 lots_ids2 = lots_obj.search(cr, uid, [('auction_id', 'in', ids), ('obj_price', '>', 0)])
112 new_seller_invoice = lots_obj.seller_trans_create(cr, uid, lots_ids2, {})
113 self.write(cr, uid, ids, {'state': 'closed'}) #close the auction
118 #----------------------------------------------------------
120 #----------------------------------------------------------
121 class auction_deposit(osv.osv):
122 """Auction Deposit Border"""
124 _name = "auction.deposit"
128 'transfer' : fields.boolean('Transfer'),
129 'name': fields.char('Depositer Inventory', size=64, required=True),
130 'partner_id': fields.many2one('res.partner', 'Seller', required=True, change_default=True),
131 'date_dep': fields.date('Deposit date', required=True),
132 'method': fields.selection((('keep', 'Keep until sold'), ('decease', 'Decrease limit of 10%'), ('contact', 'Contact the Seller')), 'Withdrawned method', required=True),
133 'tax_id': fields.many2one('account.tax', 'Expenses'),
134 'create_uid': fields.many2one('res.users', 'Created by', readonly=True),
135 'info': fields.char('Description', size=64),
136 'lot_id': fields.one2many('auction.lots', 'bord_vnd_id', 'Objects'),
137 'specific_cost_ids': fields.one2many('auction.deposit.cost', 'deposit_id', 'Specific Costs'),
138 'total_neg': fields.boolean('Allow Negative Amount'),
141 'method': lambda *a: 'keep',
142 'total_neg': lambda *a: False,
143 'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'auction.deposit'),
148 #----------------------------------------------------------
149 # (Specific) Deposit Costs
150 #----------------------------------------------------------
151 class auction_deposit_cost(osv.osv):
153 """Auction Deposit Cost"""
155 _name = 'auction.deposit.cost'
158 'name': fields.char('Cost Name', required=True, size=64),
159 'amount': fields.float('Amount'),
160 'account': fields.many2one('account.account', 'Destination Account', required=True),
161 'deposit_id': fields.many2one('auction.deposit', 'Deposit'),
163 auction_deposit_cost()
165 #----------------------------------------------------------
167 #----------------------------------------------------------
168 class aie_category(osv.osv):
173 'name': fields.char('Name', size=64, required=True),
174 'code':fields.char('Code', size=64),
175 'parent_id': fields.many2one('aie.category', 'Parent aie Category', ondelete='cascade'),
176 'child_ids': fields.one2many('aie.category', 'parent_id', help="Childs aie category")
179 def name_get(self, cr, uid, ids, context=None):
185 reads = self.read(cr, uid, ids, ['name', 'parent_id'], context)
187 name = record['name']
188 if record['parent_id']:
189 name = record['parent_id'][1] + ' / ' + name
190 res.append((record['id'], name))
195 class auction_lot_category(osv.osv):
196 """Auction Lots Category"""
198 _name = 'auction.lot.category'
201 'name': fields.char('Category Name', required=True, size=64),
202 'priority': fields.float('Priority'),
203 'active' : fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the auction lot category without removing it."),
204 'aie_categ': fields.many2one('aie.category', 'Category', ondelete='cascade'),
207 'active' : lambda *a: 1,
209 auction_lot_category()
211 #----------------------------------------------------------
213 #----------------------------------------------------------
214 def _type_get(self, cr, uid, context=None):
217 obj = self.pool.get('auction.lot.category')
218 ids = obj.search(cr, uid, [])
219 res = obj.read(cr, uid, ids, ['name'], context)
220 res = [(r['name'], r['name']) for r in res]
223 class auction_lots(osv.osv):
226 _name = "auction.lots"
227 _order = "obj_num,lot_num,id"
230 def button_not_bought(self, cr, uid, ids, context=None):
233 return self.write(cr, uid, ids, {'state':'unsold'})
235 def button_taken_away(self, cr, uid, ids, context=None):
238 return self.write(cr, uid, ids, {'state':'taken_away', 'ach_emp': True})
240 def button_unpaid(self, cr, uid, ids, context=None):
243 return self.write(cr, uid, ids, {'state':'draft'})
245 def button_bought(self, cr, uid, ids, context=None):
248 return self.write(cr, uid, ids, {'state':'sold'})
250 def _getprice(self, cr, uid, ids, fields, args, context=None):
251 """This Function compute amount total with tax for buyer and seller.
252 @param ids: List of auction lots's id
253 @param name: List of function fields.
254 @param context: A standard dictionary for contextual values
255 @return: Dictionary of function fields value.
261 account_analytic_line_obj = self.pool.get('account.analytic.line')
262 lots = self.browse(cr, uid, ids, context)
263 pt_tax = self.pool.get('account.tax')
268 res[lot.id] = {name: False}
269 amount = lot.obj_price or 0.0
271 if name == "buyer_price":
273 taxes.append(lot.author_right)
275 taxes += lot.auction_id.buyer_costs
276 tax = pt_tax.compute_all(cr, uid, taxes, amount, 1)['taxes']
278 result += t['amount']
280 elif name == "seller_price":
281 if lot.bord_vnd_id.tax_id:
282 taxes.append(lot.bord_vnd_id.tax_id)
283 elif lot.auction_id and lot.auction_id.seller_costs:
284 taxes += lot.auction_id.seller_costs
285 tax = pt_tax.compute_all(cr, uid, taxes, amount, 1)['taxes']
287 result += t['amount']
289 elif name == "gross_revenue":
291 result = lot.buyer_price - lot.seller_price
293 elif name == "net_revenue":
295 result = lot.buyer_price - lot.seller_price - lot.costs
297 elif name == "net_margin":
298 if ((lot.obj_price==0) and (lot.state=='draft')):
299 amount = lot.lot_est1
301 amount = lot.obj_price
303 result = (lot.net_revenue * 100) / amount
304 elif name == "costs":
305 # costs: Total credit of analytic account
306 # objects sold during this auction (excluding analytic lines that are in the analytic journal of the auction date)
309 auct_id = lot.auction_id.id
310 lot_count = self.search(cr, uid, [('auction_id', '=', auct_id)], count=True)
311 line_ids = account_analytic_line_obj.search(cr, uid, [
312 ('account_id', '=', lot.auction_id.account_analytic_id.id),
313 ('journal_id', '<>', lot.auction_id.journal_id.id),
314 ('journal_id', '<>', lot.auction_id.journal_seller_id.id)])
315 indir_cost = lot.bord_vnd_id.specific_cost_ids
316 for r in lot.bord_vnd_id.specific_cost_ids:
318 for line in account_analytic_line_obj.browse(cr, uid, line_ids, context=context):
321 result = som/lot_count
323 elif name=="paid_ach":
325 if lot.ach_inv_id and lot.ach_inv_id.state == 'paid':
328 elif name=="paid_vnd":
330 if lot.sel_inv_id and lot.sel_inv_id.state == 'paid':
333 res[lot.id][name] = result
338 def onchange_obj_ret(self, cr, uid, ids, obj_ret, context=None):
342 return {'value': {'obj_price': 0}}
346 'bid_lines':fields.one2many('auction.bid_line', 'lot_id', 'Bids'),
347 'auction_id': fields.many2one('auction.dates', 'Auction', select=1, help="Auction For Object"),
348 'bord_vnd_id': fields.many2one('auction.deposit', 'Depositer Inventory', required=True, help="Provide Deposit Information: seller, Withdrawned Method, Object, Deposit Costs"),
349 'name': fields.char('Title', size=64, required=True, help='Auction Object Name'),
350 'name2': fields.char('Short Description (2)', size=64),
351 'lot_type': fields.selection(_type_get, 'Object category', size=64),
352 'author_right': fields.many2one('account.tax', 'Author rights', help="Account Tax For Author Commission"),
353 'lot_est1': fields.float('Minimum Estimation', help="Minimum Estimate Price"),
354 'lot_est2': fields.float('Maximum Estimation', help="Maximum Estimate Price"),
355 'lot_num': fields.integer('List Number', required=True, select=1, help="List Number In Depositer Inventory"),
356 'create_uid': fields.many2one('res.users', 'Created by', readonly=True),
357 'history_ids':fields.one2many('auction.lot.history', 'lot_id', 'Auction history'),
358 'lot_local':fields.char('Location', size=64, help="Auction Location"),
359 'artist_id':fields.many2one('auction.artists', 'Artist/Author'),
360 'artist2_id':fields.many2one('auction.artists', 'Artist/Author2'),
361 'important':fields.boolean('To be Emphatized'),
362 'product_id':fields.many2one('product.product', 'Product', required=True),
363 'obj_desc': fields.text('Object Description'),
364 'obj_num': fields.integer('Catalog Number'),
365 'obj_ret': fields.float('Price retired', help="Object Ret"),
366 'obj_comm': fields.boolean('Commission'),
367 'obj_price': fields.float('Adjudication price', help="Object Price"),
368 'ach_avance': fields.float('Buyer Advance'),
369 'ach_login': fields.char('Buyer Username', size=64),
370 'ach_uid': fields.many2one('res.partner', 'Buyer'),
371 'seller_id': fields.related('bord_vnd_id','partner_id', type='many2one', relation='res.partner', string='Seller', readonly=True),
372 'ach_emp': fields.boolean('Taken Away', readonly=True, help="When This Field is True means, Objects is taken away by Buyer"),
373 'is_ok': fields.boolean('Buyer\'s payment', help="When Buyer Pay For Bank statement', This field is selected as True."),
374 'ach_inv_id': fields.many2one('account.invoice', 'Buyer Invoice', readonly=True, states={'draft':[('readonly', False)]}),
375 'sel_inv_id': fields.many2one('account.invoice', 'Seller Invoice', readonly=True, states={'draft':[('readonly', False)]}),
376 'vnd_lim': fields.float('Seller limit'),
377 'vnd_lim_net': fields.boolean('Net limit ?', readonly=True),
378 'image': fields.binary('Image', help="Object Image"),
379 'paid_vnd':fields.function(_getprice, string='Seller Paid', method=True, type='boolean', store=True, multi="paid_vnd", help="When state of Seller Invoice is 'Paid', This field is selected as True."),
380 'paid_ach':fields.function(_getprice, string='Buyer Invoice Reconciled', method=True, type='boolean', store=True, multi="paid_ach", help="When state of Buyer Invoice is 'Paid', This field is selected as True."),
381 'state': fields.selection((
383 ('unsold', 'Unsold'),
386 ('taken_away', 'Taken away')), 'State', required=True, readonly=True,
387 help=' * The \'Draft\' state is used when a object is encoding as a new object. \
388 \n* The \'Unsold\' state is used when object does not sold for long time, user can also set it as draft state after unsold. \
389 \n* The \'Paid\' state is used when user pay for the object \
390 \n* The \'Sold\' state is used when user buy the object.'),
391 'buyer_price': fields.function(_getprice, method=True, string='Buyer price', store=True, multi="buyer_price", help="Buyer Price"),
392 'seller_price': fields.function(_getprice, method=True, string='Seller price', store=True, multi="seller_price", help="Seller Price"),
393 'gross_revenue':fields.function(_getprice, method=True, string='Gross revenue', store=True, multi="gross_revenue", help="Buyer Price - Seller Price"),
394 'gross_margin':fields.function(_getprice, method=True, string='Gross Margin (%)', store=True, multi="gross_margin", help="Gross Income Divided by Net Sales"),
395 'costs':fields.function(_getprice, method=True, string='Indirect costs', store=True, multi="costs", help="Total Credit of analytic account"),
396 'statement_id': fields.many2many('account.bank.statement.line', 'auction_statement_line_rel', 'auction_id', 'statement', 'Payment', help="Bank statement Line For Given Buyer"),
397 'net_revenue':fields.function(_getprice, method=True, string='Net revenue', store=True, multi="net_revenue", help="Buyer Price - Seller Price - Indirect Cost"),
398 'net_margin':fields.function(_getprice, method=True, string='Net Margin (%)', store=True, multi="net_margin", help="(Net Revenue * 100)/ Object Price"),
401 'state':lambda *a: 'draft',
402 'lot_num':lambda *a:1,
403 'is_ok': lambda *a: False,
406 def name_get(self, cr, user, ids, context=None):
411 result = [ (r['id'], str(r['obj_num'])+' - '+r['name']) for r in self.read(cr, user, ids, ['name', 'obj_num'])]
414 def name_search(self, cr, user, name, args=None, operator='ilike', context=None):
421 ids = self.search(cr, user, [('obj_num', '=', int(name))] + args)
423 ids = self.search(cr, user, [('name', operator, name)] + args)
424 return self.name_get(cr, user, ids)
426 def _sum_taxes_by_type_and_id(self, taxes):
428 PARAMS: taxes: a list of dictionaries of the form {'id':id, 'amount':amount, ...}
429 RETURNS : a list of dictionaries of the form {'id':id, 'amount':amount, ...}; one dictionary per unique id.
430 The others fields in the dictionaries (other than id and amount) are those of the first tax with a particular id.
434 key = (tax['type'], tax['id'])
435 if key in taxes_summed:
436 taxes_summed[key]['amount'] += tax['amount']
438 taxes_summed[key] = tax
439 return taxes_summed.values()
441 def compute_buyer_costs(self, cr, uid, ids):
443 lots = self.browse(cr, uid, ids)
444 ##CHECKME: Is that AC would be worthwhile to make groups of lots that have the same costs to spend a lot of lists compute?
447 pt_tax = self.pool.get('account.tax')
449 taxes = lot.product_id.taxes_id
451 taxes.append(lot.author_right)
453 taxes += lot.auction_id.buyer_costs
454 tax=pt_tax.compute_all(cr, uid, taxes, lot.obj_price, 1)['taxes']
457 amount_total['value']= amount
458 amount_total['amount']= amount
463 def _compute_lot_seller_costs(self, cr, uid, lot, manual_only=False):
467 border_id = lot.bord_vnd_id
470 tax_cost_ids.append(border_id.tax_id)
471 elif lot.auction_id and lot.auction_id.seller_costs:
472 tax_cost_ids += lot.auction_id.seller_costs
474 tax_costs = self.pool.get('account.tax').compute_all(cr, uid, tax_cost_ids, lot.obj_price, 1)['taxes']
475 # delete useless keys from the costs computed by the tax object... this is useless but cleaner...
476 for cost in tax_costs:
477 del cost['account_paid_id']
478 del cost['account_collected_id']
481 costs.extend(tax_costs)
483 c.update({'type': 0})
485 if lot.vnd_lim_net<0 and lot.obj_price>0:
486 #FIXME:the string passes have lot 'should go through the system translations.
487 obj_price_wh_costs = reduce(lambda x, y: x + y['amount'], tax_costs, lot.obj_price)
488 if obj_price_wh_costs < lot.vnd_lim:
489 costs.append({ 'type': 1,
491 'name': 'Remise lot '+ str(lot.obj_num),
492 'amount': lot.vnd_lim - obj_price_wh_costs}
496 def compute_seller_costs(self, cr, uid, ids, manual_only=False):
497 lots = self.browse(cr, uid, ids)
500 # group objects (lots) by deposit id
501 # ie create a dictionary containing lists of objects
504 key = lot.bord_vnd_id.id
505 if not key in bord_lots:
507 bord_lots[key].append(lot)
509 # use each list of object in turn
510 for lots in bord_lots.values():
514 total_adj += lot.obj_price or 0.0
515 lot_costs = self._compute_lot_seller_costs(cr, uid, lot, manual_only)
517 total_cost += c['amount']
518 costs.extend(lot_costs)
519 bord = lots[0].bord_vnd_id
521 if bord.specific_cost_ids:
522 bord_costs = [{'type':2, 'id':c.id, 'name':c.name, 'amount':c.amount, 'account_id':c.account} for c in bord.specific_cost_ids]
524 total_cost += c['amount']
525 costs.extend(bord_costs)
526 if (total_adj+total_cost)<0:
527 #FIXME: translate tax name
528 new_id = bord and bord.id or 0
529 c = {'type':3, 'id':new_id, 'amount':-total_cost-total_adj, 'name':'Ristourne'}
531 return self._sum_taxes_by_type_and_id(costs)
533 # sum remise limite net and ristourne
534 def compute_seller_costs_summed(self, cr, uid, ids): #ach_pay_id
536 """This Fuction sum Net remittance limit and refund"""
538 taxes = self.compute_seller_costs(cr, uid, ids)
543 #FIXME: translate tax names
544 tax['name'] = 'Discount sharp boundary'
545 elif tax['type'] == 2:
547 tax['name'] = 'Miscellaneous expenditure'
548 elif tax['type'] == 3:
550 tax['name'] = 'Cross.'
551 key = (tax['type'], tax['id'])
552 if key in taxes_summed:
553 taxes_summed[key]['amount'] += tax['amount']
555 taxes_summed[key] = tax
556 return taxes_summed.values()
558 def buyer_proforma(self, cr, uid, ids, context=None):
563 inv_ref = self.pool.get('account.invoice')
564 res_obj = self.pool.get('res.partner')
565 inv_line_obj = self.pool.get('account.invoice.line')
566 wf_service = netsvc.LocalService('workflow')
567 for lot in self.browse(cr, uid, ids, context):
568 if not lot.obj_price>0:
570 if not lot.ach_uid.id:
571 raise orm.except_orm(_('Missed buyer !'), _('The object "%s" has no buyer assigned.') % (lot.name,))
573 partner_ref =lot.ach_uid.id
574 lot_name = lot.obj_num
575 res = res_obj.address_get(cr, uid, [partner_ref], ['contact', 'invoice'])
576 contact_addr_id = res['contact']
577 invoice_addr_id = res['invoice']
578 if not invoice_addr_id:
579 raise orm.except_orm(_('No Invoice Address'), _('The Buyer "%s" has no Invoice Address.') % (contact_addr_id,))
581 'name': 'Auction proforma:' +lot.name,
582 'journal_id': lot.auction_id.journal_id.id,
583 'partner_id': partner_ref,
584 'type': 'out_invoice',
585 # 'state':'proforma',
587 inv.update(inv_ref.onchange_partner_id(cr, uid, [], 'out_invoice', partner_ref)['value'])
588 inv['account_id'] = inv['account_id'] and inv['account_id'][0]
589 inv_id = inv_ref.create(cr, uid, inv, context)
590 invoices[partner_ref] = inv_id
591 self.write(cr, uid, [lot.id], {'ach_inv_id':inv_id, 'state':'sold'})
594 taxes = map(lambda x: x.id, lot.product_id.taxes_id)
595 taxes+=map(lambda x:x.id, lot.auction_id.buyer_costs)
597 taxes.append(lot.author_right.id)
600 'invoice_id': inv_id,
602 'product_id': lot.product_id.id,
603 'name': 'proforma'+'['+str(lot.obj_num)+'] '+ lot.name,
604 'invoice_line_tax_id': [(6, 0, taxes)],
605 'account_analytic_id': lot.auction_id.account_analytic_id.id,
606 'account_id': lot.auction_id.acc_income.id,
607 'price_unit': lot.obj_price,
609 inv_line_obj.create(cr, uid, inv_line, context)
610 inv_ref.button_compute(cr, uid, invoice.values())
611 wf_service.trg_validate(uid, 'account.invoice', inv_id, 'invoice_proforma2', cr)
612 return invoices.values()
614 # creates the transactions between the auction company and the seller
615 # this is done by creating a new in_invoice for each
616 def seller_trans_create(self, cr, uid, ids, context=None):
618 Create a seller invoice for each bord_vnd_id, for selected ids.
620 # use each list of object in turn
624 inv_ref=self.pool.get('account.invoice')
625 partner_obj = self.pool.get('res.partner')
626 inv_line_obj = self.pool.get('account.invoice.line')
627 wf_service = netsvc.LocalService('workflow')
628 for lot in self.browse(cr, uid, ids, context):
629 partner_id = lot.bord_vnd_id.partner_id.id
630 if not lot.auction_id.id:
632 lot_name = lot.obj_num
633 if lot.bord_vnd_id.id in invoices:
634 inv_id = invoices[lot.bord_vnd_id.id]
636 res = partner_obj.address_get(cr, uid, [lot.bord_vnd_id.partner_id.id], ['contact', 'invoice'])
637 contact_addr_id = res['contact']
638 invoice_addr_id = res['invoice']
640 'name': 'Auction:' +lot.name,
641 'journal_id': lot.auction_id.journal_seller_id.id,
642 'partner_id': lot.bord_vnd_id.partner_id.id,
643 'type': 'in_invoice',
645 inv.update(inv_ref.onchange_partner_id(cr, uid, [], 'in_invoice', lot.bord_vnd_id.partner_id.id)['value'])
646 inv_id = inv_ref.create(cr, uid, inv, context)
647 invoices[lot.bord_vnd_id.id] = inv_id
649 self.write(cr, uid, [lot.id], {'sel_inv_id':inv_id, 'state':'sold'})
651 taxes = map(lambda x: x.id, lot.product_id.taxes_id)
652 if lot.bord_vnd_id.tax_id:
653 taxes.append(lot.bord_vnd_id.tax_id.id)
655 taxes += map(lambda x: x.id, lot.auction_id.seller_costs)
658 'invoice_id': inv_id,
660 'product_id': lot.product_id.id,
661 'name': '['+str(lot.obj_num)+'] '+lot.auction_id.name,
662 'invoice_line_tax_id': [(6, 0, taxes)],
663 'account_analytic_id': lot.auction_id.account_analytic_id.id,
664 'account_id': lot.auction_id.acc_expense.id,
665 'price_unit': lot.obj_price,
667 inv_line_obj.create(cr, uid, inv_line, context)
668 inv_ref.button_compute(cr, uid, invoices.values())
669 for inv in inv_ref.browse(cr, uid, invoices.values(), context):
670 inv_ref.write(cr, uid, [inv.id], {
671 'check_total': inv.amount_total
673 wf_service.trg_validate(uid, 'account.invoice', inv.id, 'invoice_open', cr)
674 return invoices.values()
676 def lots_invoice(self, cr, uid, ids, context, invoice_number=False):
678 Create an invoice for selected lots (IDS) to BUYER_ID.
679 Set created invoice to the ACTION state.
683 xxxxx: set the invoice state to ACTION
685 RETURN: id of generated invoice
687 dt = time.strftime('%Y-%m-%d')
688 inv_ref = self.pool.get('account.invoice')
689 res_obj = self.pool.get('res.partner')
690 inv_line_obj = self.pool.get('account.invoice.line')
691 wf_service = netsvc.LocalService('workflow')
693 for lot in self.browse(cr, uid, ids, context):
694 if not lot.auction_id.id:
696 if not lot.ach_uid.id:
697 raise orm.except_orm(_('Missed buyer !'), _('The object "%s" has no buyer assigned.') % (lot.name,))
698 if (lot.auction_id.id, lot.ach_uid.id) in invoices:
699 inv_id = invoices[(lot.auction_id.id, lot.ach_uid.id)]
701 add = res_obj.read(cr, uid, [lot.ach_uid.id], ['address'])[0]['address']
703 raise orm.except_orm(_('Missed Address !'), _('The Buyer has no Invoice Address.'))
704 price = lot.obj_price or 0.0
705 lot_name =lot.obj_num
707 'name':lot.auction_id.name or '',
708 'reference': lot.ach_login,
709 'journal_id': lot.auction_id.journal_id.id,
710 'partner_id': lot.ach_uid.id,
711 'type': 'out_invoice',
714 inv['number'] = invoice_number
715 inv.update(inv_ref.onchange_partner_id(cr, uid, [], 'out_invoice', lot.ach_uid.id)['value'])
716 inv_id = inv_ref.create(cr, uid, inv, context)
717 invoices[(lot.auction_id.id, lot.ach_uid.id)] = inv_id
718 self.write(cr, uid, [lot.id], {'ach_inv_id':inv_id, 'state':'sold'})
720 taxes = map(lambda x: x.id, lot.product_id.taxes_id)
721 taxes+=map(lambda x:x.id, lot.auction_id.buyer_costs)
723 taxes.append(lot.author_right.id)
726 'invoice_id': inv_id,
728 'product_id': lot.product_id.id,
729 'name': '['+str(lot.obj_num)+'] '+ lot.name,
730 'invoice_line_tax_id': [(6, 0, taxes)],
731 'account_analytic_id': lot.auction_id.account_analytic_id.id,
732 'account_id': lot.auction_id.acc_income.id,
733 'price_unit': lot.obj_price,
735 inv_line_obj.create(cr, uid, inv_line, context)
736 inv_ref.button_compute(cr, uid, [inv_id])
737 for l in inv_ref.browse(cr, uid, invoices.values(), context):
738 wf_service.trg_validate(uid, 'account.invoice', l.id, 'invoice_open', cr)
739 return invoices.values()
743 #----------------------------------------------------------
745 #----------------------------------------------------------
746 class auction_bid(osv.osv):
749 _name = "auction.bid"
753 'partner_id': fields.many2one('res.partner', 'Buyer Name', required=True),
754 'contact_tel':fields.char('Contact', size=64),
755 'name': fields.char('Bid ID', size=64, required=True),
756 'auction_id': fields.many2one('auction.dates', 'Auction Date', required=True),
757 'bid_lines': fields.one2many('auction.bid_line', 'bid_id', 'Bid'),
760 'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'auction.bid'),
763 def onchange_contact(self, cr, uid, ids, partner_id):
765 return {'value': {'contact_tel':False}}
766 contact = self.pool.get('res.partner').browse(cr, uid, partner_id)
767 if len(contact.address):
768 v_contact=contact.address[0] and contact.address[0].phone
771 return {'value': {'contact_tel': v_contact}}
775 class auction_lot_history(osv.osv):
778 _name = "auction.lot.history"
781 'name': fields.date('Date', size=64),
782 'lot_id': fields.many2one('auction.lots', 'Object', required=True, ondelete='cascade'),
783 'auction_id': fields.many2one('auction.dates', 'Auction date', required=True, ondelete='cascade'),
784 'price': fields.float('Withdrawn price', digits=(16, 2))
787 'name': lambda *args: time.strftime('%Y-%m-%d')
789 auction_lot_history()
791 class auction_bid_lines(osv.osv):
792 _name = "auction.bid_line"
796 'name': fields.char('Bid date', size=64),
797 'bid_id': fields.many2one('auction.bid', 'Bid ID', required=True, ondelete='cascade'),
798 'lot_id': fields.many2one('auction.lots', 'Object', required=True, ondelete='cascade'),
799 'call': fields.boolean('To be Called'),
800 'price': fields.float('Maximum Price'),
801 'auction': fields.char(string='Auction Name', size=64)
804 'name': lambda *args: time.strftime('%Y-%m-%d')
807 def onchange_name(self, cr, uid, ids, lot_id):
809 return {'value': {'auction':False}}
810 auctions = self.pool.get('auction.lots').browse(cr, uid, lot_id)
811 v_auction=auctions.auction_id.name or False
812 return {'value': {'auction': v_auction}}
817 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: