def do_partial(self, cr, uid, ids, context=None): """ Makes partial moves and pickings done. @param self: The object pointer. @param cr: A database cursor @param uid: ID of the user currently logged in @param fields: List of fields for which we want default values @param context: A standard dictionary @return: A dictionary which of fields with values. """ pick_obj = self.pool.get('stock.picking') uom_obj = self.pool.get('product.uom') picking_ids = context.get('active_ids', False) partial = self.browse(cr, uid, ids[0], context=context) partial_datas = { 'delivery_date' : partial.date } for pick in pick_obj.browse(cr, uid, picking_ids, context=context): picking_type = self.get_picking_type(cr, uid, pick, context=context) moves_list = picking_type == 'in' and partial.product_moves_in or partial.product_moves_out for move in moves_list: #Adding a check whether any line has been added with new qty if not move.move_id: raise osv.except_osv(_('Processing Error'),\ _('You cannot add any new move while validating the picking, rather you can split the lines prior to validation!')) calc_qty = uom_obj._compute_qty(cr, uid, move.product_uom.id, \ move.quantity, move.move_id.product_uom.id) #Adding a check whether any move line contains exceeding qty to original moveline product_uom = self.pool.get('decimal.precision').precision_get(cr, uid, 'Product UOM') if round(calc_qty, product_uom) > round(move.move_id.product_qty, product_uom): precision = '%0.' + str(dp.get_precision('Product UoM')(cr)[1] or 0) + 'f' raise osv.except_osv(_('Processing Error'), _('Processing quantity %s %s for %s is larger than the available quantity %s %s !')\ % (precision % calc_qty, move.product_uom.name, move.product_id.name,\ precision % move.move_id.product_qty, move.move_id.product_uom.name)) #Adding a check whether any move line contains qty less than zero if calc_qty < 0: precision = '%0.' + str(dp.get_precision('Product UoM')(cr)[1] or 0) + 'f' raise osv.except_osv(_('Processing Error'), \ _('Can not process quantity %s for Product %s !') \ % (precision % move.quantity, move.product_id.name)) partial_datas['move%s' % (move.move_id.id)] = { 'product_id': move.product_id.id, 'product_qty': calc_qty, 'product_uom': move.move_id.product_uom.id, 'prodlot_id': move.prodlot_id.id, } if (picking_type == 'in') and (move.product_id.cost_method == 'average'): partial_datas['move%s' % (move.move_id.id)].update({ 'product_price' : move.cost, 'product_currency': move.currency.id, }) pick_obj.do_partial(cr, uid, picking_ids, partial_datas, context=context) return {'type': 'ir.actions.act_window_close'}
def agg_righe_iva(self, cr, uid, ids, context): def get_perc_iva(self, cr, uid, ids, idiva, context): dati = self.pool.get("account.tax").read(cr, uid, [idiva], (["amount", "type"]), context=context) return dati[0]["amount"] res = super(FiscalDocIva, self).agg_righe_iva( cr, uid, ids, context ) # prima ricalcola il castelletto iva standard res = self.pool.get("conai.castelletto").agg_tot_conai(cr, uid, ids, context) conai_ids = self.pool.get("conai.castelletto").search(cr, uid, [("name", "=", ids)]) if conai_ids: # ci sono righe di castelletto conai for riga_cast in self.pool.get("conai.castelletto").browse(cr, uid, conai_ids): iva_id = self.pool.get("fiscaldoc.iva").search( cr, uid, [("name", "=", riga_cast.name.id), ("codice_iva", "=", riga_cast.codice_iva.id)] ) if iva_id: # somma il solo imponibile iva = {} iva["imponibile"] = ( self.pool.get("fiscaldoc.iva").browse(cr, uid, iva_id[0]).imponibile + riga_cast.totale_conai ) ok = self.pool.get("fiscaldoc.iva").write(cr, uid, [iva_id[0]], iva) # ora ricalcola l'imposta righe_iva = self.pool.get("fiscaldoc.iva").search(cr, uid, [("name", "=", ids)]) for rg_iva in self.pool.get("fiscaldoc.iva").browse(cr, uid, righe_iva): perc_iva = get_perc_iva(self, cr, uid, ids, rg_iva.codice_iva.id, context) imposta = rg_iva.imponibile * perc_iva imposta = arrot(cr, uid, imposta, dp.get_precision("Account")) ok = self.pool.get("fiscaldoc.iva").write(cr, uid, [rg_iva.id], {"imposta": imposta})
def balance_move(self, cr, uid, move_id, context=None): currency_obj = self.pool.get('res.currency') move = self.pool.get('account.move').browse(cr, uid, move_id, context) amount = 0.0 for line in move.line_id: amount += line.debit - line.credit amount = currency_obj.round(cr, uid, move.company_id.currency_id, amount) # check if balance differs for more than 1 decimal according to account decimal precision if abs(amount * 10 ** dp.get_precision('Account')(cr)[1]) > 1: raise orm.except_orm(_('Error'), _('The generated payment entry is unbalanced for more than 1 decimal')) if not currency_obj.is_zero(cr, uid, move.company_id.currency_id, amount): for line in move.line_id: # adjust the first move line that's not receivable, payable or liquidity if line.account_id.type != 'receivable' and line.account_id.type != 'payable' and line.account_id.type != 'liquidity': if line.credit: line.write({ 'credit': line.credit + amount, }, update_check=False) elif line.debit: line.write({ 'debit': line.debit - amount, }, update_check=False) if line.tax_amount: line.write({ 'tax_amount': line.tax_amount + amount, }, update_check=False) break return amount
def _link_payment(self, cursor, uid, trans, payment_lines, partner_ids, bank_account_ids, log, linked_payments): ''' Find the payment order belonging to this reference - if there is one This is the easiest part: when sending payments, the returned bank info should be identical to ours. ''' # TODO: Not sure what side effects are created when payments are done # for credited customer invoices, which will be matched later on too. digits = dp.get_precision('Account')(cursor)[1] candidates = [x for x in payment_lines if x.communication == trans.reference and round(x.amount, digits) == -round(trans.transferred_amount, digits) and trans.remote_account in (x.bank_id.acc_number, x.bank_id.iban) ] if len(candidates) == 1: candidate = candidates[0] # Check cache to prevent multiple matching of a single payment if candidate.id not in linked_payments: linked_payments[candidate.id] = True payment_line_obj = self.pool.get('payment.line') payment_line_obj.write(cursor, uid, [candidate.id], { 'export_state': 'done', 'date_done': trans.effective_date.strftime('%Y-%m-%d')} ) return self._get_move_info(cursor, uid, candidate.move_line_id) return False
def generadist(self, cr, uid, ids, context=None): Parametri = self.browse(cr, uid, ids)[0] cerca = [ ('data_scadenza', '>=', Parametri.da_data_scadenza), ('data_scadenza', '<=', Parametri.a_data_scadenza), ('distinta', '=', None), ] ids_eff = self.pool.get('effetti').search(cr, uid, cerca,order='data_scadenza') First = True totale_distinta = 0 if ids_eff: for effetto in self.pool.get('effetti').browse(cr, uid, ids_eff): if not effetto.distinta or len(effetto.distinta)==0: if First: First = False # crea l'id della distinta testa_distinta = { 'banca_pres':Parametri.banca_pres.id, 'data_distinta':Parametri.data_distinta, 'st_anag_compl':Parametri.st_anag_compl, 'data_presentazione':Parametri.data_presentazione, 'note':'Distinta' } id_distinta = self.pool.get('distinte.effetti').create(cr, uid, testa_distinta) if totale_distinta + effetto.importo_effetto <= Parametri.totale_importo: totale_distinta += arrot(cr,uid,effetto.importo_effetto,dp.get_precision('Account')) riga_distinta = { 'name':id_distinta, 'effetto_id':effetto.id, } id_riga_distinta = self.pool.get('distinte.effetti.righe').create(cr, uid, riga_distinta) agg_effetto = { 'distinta':self.pool.get('distinte.effetti').browse(cr, uid, [id_distinta])[0].name, } #import pdb;pdb.set_trace() ok = self.pool.get('effetti').write(cr, uid, [effetto.id], agg_effetto) # Scrive l'importo totale della distinta testa_distinta = { 'totale_distinta':totale_distinta, } ok = self.pool.get('distinte.effetti').write(cr, uid, [id_distinta], testa_distinta) return { 'name': _('Distinte Effetti'), 'view_type': 'form', 'view_mode': 'form,tree', 'res_model': 'distinte.effetti', 'res_id':id_distinta, 'view_id': False, 'context': context, 'type': 'ir.actions.act_window', } else: raise osv.except_osv(_('ERRORE !'), _('Non ci sono Effetti per questa selezione ')) return {'type': 'ir.actions.act_window_close'} # non va bene deve aprire la finestra degli effetti
def float_round(cr,f_val,application = None): """ 使用decimal precision 设置ktv_fee,四舍五入给定的float """ dp_name = application if application else "ktv_fee" dp_compute = dp.get_precision(dp_name) precision,scale = dp_compute(cr) ret = tools.float_round(f_val,precision_digits=scale) return ret
def _tot_riga_conai(self, cr, uid, ids, field_name, arg, context=None): res = {} if context is None: context = {} for line in self.browse(cr, uid, ids, context=context): if ( line.name.esenzione_conai and line.name.scad_esenzione_conai >= line.name.data_documento ): # c'è una esenzione conai # c'è un codice di esenzione res[line.id] = line.prezzo_conai * line.peso_conai res[line.id] = res[line.id] * (1 - line.name.esenzione_conai.perc / 100) else: res[line.id] = line.prezzo_conai * line.peso_conai res[line.id] = arrot(cr, uid, res[line.id], dp.get_precision("Account")) # arrotonda a 2 cifre in genere return res
class structural_costs_impact_wizard(osv.osv_memory): """ Wizard to percentually impact structural costs on service products """ def onchange_analytic_account(self, cr, uid, ids, fiscalyear_id, period_id, account_id): """ Gets total amount from chosen analytical account and year """ res = {} context = {} res['structural_cost'] = 0.0 struct_cost_facade = self.pool.get('product.percent.struct.costs') sales_facade = self.pool.get('sale.order') if account_id: fiscalyear = self.pool.get('account.fiscalyear').browse( cr, uid, fiscalyear_id) if not period_id: context['from_date'] = fiscalyear.date_start context['to_date'] = fiscalyear.date_stop else: period = self.pool.get('account.period').browse( cr, uid, period_id) context['from_date'] = period.date_start context['to_date'] = period.date_stop account = self.pool.get('account.analytic.account').browse( cr, uid, account_id, context) res['structural_cost'] = account.credit #Forces creation of product lines based on sales made on chosen period and fiscal year... period_sales = sales_facade.browse( cr, uid, sales_facade.search( cr, uid, [('state', 'not in', ['draft', 'cancel']), ('date_order', '<=', context['to_date']), ('date_order', '>=', context['from_date'])])) distinct_sale_products = {} product_line_ids = [] for sale in period_sales: for line in sale.order_line: if line.product_id: if line.product_id.id not in distinct_sale_products.keys( ): distinct_sale_products[ line.product_id.id] = line.product_uom_qty else: distinct_sale_products[ line.product_id.id] += line.product_uom_qty for product_id in distinct_sale_products.keys(): vals_product_line = { 'product_id': product_id, 'total_sales': distinct_sale_products[product_id], 'forecasted_sales': distinct_sale_products[product_id], #'wizard_id': self.next_id + 1 } #line_id = struct_cost_facade.create(cr, uid, vals_product_line) product_line_ids.append(vals_product_line) res['products_percent'] = product_line_ids return {'value': res} def onchange_cost_method(self, cr, uid, ids, cost_method, structural_cost, products_percent): """ Gets amount to impact over chosen products based on sales forecast """ res = {} res['cost_to_impact'] = 0.0 sum_forecasted_sales = 0.0 if cost_method == 'uniform': for product_line in products_percent: sum_forecasted_sales += product_line[2]['forecasted_sales'] res['cost_to_impact'] = structural_cost / sum_forecasted_sales return {'value': res} def _get_current_user_company(self, cr, uid, context={}): """ Obtiene la compañía del usuario activo """ current_user = self.pool.get('res.users').browse(cr, uid, uid) return current_user.company_id.id def _get_previous_fiscalyear(self, cr, uid, context): """ Get previous current year """ fyear_facade = self.pool.get('account.fiscalyear') current_fyear = context and context.get( 'fiscalyear_id', None) or fyear_facade.browse(cr, uid, uid) try: prev_date_start = '%s-01-01' % ( str(int(current_fyear.date_start[0:4]) - 1)) except Exception: raise osv.except_osv(_('Error!'), _('Are fiscal years already defined...?')) prev_date_end = '%s-12-31' % ( str(int(current_fyear.date_start[0:4]) - 1)) prev_fyear = fyear_facade.search( cr, uid, [('company_id', '=', current_fyear.company_id.id), ('date_start', '>=', prev_date_start), ('date_stop', '<=', prev_date_end)]) prev_fyear = prev_fyear and prev_fyear[0] or None if not prev_fyear: return current_fyear.id else: return prev_fyear def action_impact_struct_costs(self, cr, uid, ids, *args): """ Simply opens a list view of all modified products """ modified_prod_ids = [] view_facade = self.pool.get('ir.ui.view') product_facade = self.pool.get('product.product') for wizard in self.browse(cr, uid, ids): for line in wizard.products_percent: product_facade.write( cr, uid, line.product_id.id, {'structural_cost': wizard.cost_to_impact}) modified_prod_ids.append(line.product_id.id) product_list_view_id = view_facade.search( cr, uid, [('name', '=', 'view.add.costs.product.view.list')])[0] product_form_view_id = view_facade.search( cr, uid, [('name', '=', 'view.add.costs.product.view.form')])[0] #Finalmente, devolvemos la vista correspondiente... return { 'name': _('Impacted Products'), 'type': 'ir.actions.act_window', 'res_model': 'product.product', 'view_type': 'form', 'view_mode': 'tree,form', 'domain': "[('id', 'in', %s)]" % modified_prod_ids, 'view_id': False, 'views': [(product_list_view_id, 'tree'), (product_form_view_id, 'form'), (False, 'calendar'), (False, 'graph')], } _name = 'structural.costs.impact.wizard' _description = "Structural costs impact Wizard" _columns = { 'prev_fyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year to Look', required=True), 'prev_period_id': fields.many2one('account.period', 'Period'), 'struct_analytic_acc_id': fields.many2one('account.analytic.account', 'Structural Expenses Analytic Account'), 'structural_cost': fields.float('Structural Costs', digits_compute=dp.get_precision('Account')), 'products_percent': fields.one2many( 'product.percent.struct.costs', 'wizard_id', 'Sold Products during chosen fiscal year and/or period'), 'structural_cost_method': fields.selection( COST_METHODS, 'Cost Method', required=True, help= 'Uniform method: all costs are distributed equally amongst products.' ), 'cost_to_impact': fields.float('Cost over products', digits_compute=dp.get_precision('Account'), help='Cost based on forecasted sales.'), 'company_id': fields.many2one('res.company', 'Company') } _defaults = { #'prev_fyear_id': _get_previous_fiscalyear, 'company_id': lambda self, cr, uid, context: self._get_current_user_company( cr, uid, context), 'structural_cost_method': lambda *a: 'novalid' }
class product_product(osv.osv): def view_header_get(self, cr, uid, view_id, view_type, context=None): if context is None: context = {} res = super(product_product, self).view_header_get(cr, uid, view_id, view_type, context) if (context.get('categ_id', False)): return _('Products: ') + self.pool.get('product.category').browse( cr, uid, context['categ_id'], context=context).name return res def _product_price(self, cr, uid, ids, name, arg, context=None): res = {} if context is None: context = {} quantity = context.get('quantity') or 1.0 pricelist = context.get('pricelist', False) if pricelist: for id in ids: try: price = self.pool.get('product.pricelist').price_get( cr, uid, [pricelist], id, quantity, context=context)[pricelist] except: price = 0.0 res[id] = price for id in ids: res.setdefault(id, 0.0) return res def _get_product_available_func(states, what): def _product_available(self, cr, uid, ids, name, arg, context=None): return {}.fromkeys(ids, 0.0) return _product_available _product_qty_available = _get_product_available_func(('done', ), ('in', 'out')) _product_virtual_available = _get_product_available_func( ('confirmed', 'waiting', 'assigned', 'done'), ('in', 'out')) _product_outgoing_qty = _get_product_available_func( ('confirmed', 'waiting', 'assigned'), ('out', )) _product_incoming_qty = _get_product_available_func( ('confirmed', 'waiting', 'assigned'), ('in', )) def _product_lst_price(self, cr, uid, ids, name, arg, context=None): res = {} product_uom_obj = self.pool.get('product.uom') for id in ids: res.setdefault(id, 0.0) for product in self.browse(cr, uid, ids, context=context): if 'uom' in context: uom = product.uos_id or product.uom_id res[product.id] = product_uom_obj._compute_price( cr, uid, uom.id, product.list_price, context['uom']) else: res[product.id] = product.list_price res[product.id] = (res[product.id] or 0.0) * ( product.price_margin or 1.0) + product.price_extra return res def _get_partner_code_name(self, cr, uid, ids, product, partner_id, context=None): for supinfo in product.seller_ids: if supinfo.name.id == partner_id: return { 'code': supinfo.product_code or product.default_code, 'name': supinfo.product_name or product.name, 'variants': '' } res = { 'code': product.default_code, 'name': product.name, 'variants': product.variants } return res def _product_code(self, cr, uid, ids, name, arg, context=None): res = {} if context is None: context = {} for p in self.browse(cr, uid, ids, context=context): res[p.id] = self._get_partner_code_name(cr, uid, [], p, context.get( 'partner_id', None), context=context)['code'] return res def _product_partner_ref(self, cr, uid, ids, name, arg, context=None): res = {} if context is None: context = {} for p in self.browse(cr, uid, ids, context=context): data = self._get_partner_code_name(cr, uid, [], p, context.get('partner_id', None), context=context) if not data['variants']: data['variants'] = p.variants if not data['code']: data['code'] = p.code if not data['name']: data['name'] = p.name res[p.id] = (data['code'] and ('['+data['code']+'] ') or '') + \ (data['name'] or '') + (data['variants'] and (' - '+data['variants']) or '') return res _defaults = { 'active': lambda *a: 1, 'price_extra': lambda *a: 0.0, 'price_margin': lambda *a: 1.0, } _name = "product.product" _description = "Product" _table = "product_product" _inherits = {'product.template': 'product_tmpl_id'} _order = 'default_code,name_template' _columns = { 'qty_available': fields.function(_product_qty_available, method=True, type='float', string='Real Stock'), 'virtual_available': fields.function(_product_virtual_available, method=True, type='float', string='Virtual Stock'), 'incoming_qty': fields.function(_product_incoming_qty, method=True, type='float', string='Incoming'), 'outgoing_qty': fields.function(_product_outgoing_qty, method=True, type='float', string='Outgoing'), 'price': fields.function(_product_price, method=True, type='float', string='Pricelist', digits_compute=dp.get_precision('Sale Price')), 'lst_price': fields.function(_product_lst_price, method=True, type='float', string='Public Price', digits_compute=dp.get_precision('Sale Price')), 'code': fields.function(_product_code, method=True, type='char', string='Reference'), 'partner_ref': fields.function(_product_partner_ref, method=True, type='char', string='Customer ref'), 'default_code': fields.char('Reference', size=64), 'active': fields.boolean( 'Active', help= "If the active field is set to False, it will allow you to hide the product without removing it." ), 'variants': fields.char('Variants', size=64), 'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True, ondelete="cascade"), 'ean13': fields.char('EAN13', size=13), 'packaging': fields.one2many( 'product.packaging', 'product_id', 'Logistical Units', help= "Gives the different ways to package the same product. This has no impact on the picking order and is mainly used if you use the EDI module." ), 'price_extra': fields.float('Variant Price Extra', digits_compute=dp.get_precision('Sale Price')), 'price_margin': fields.float('Variant Price Margin', digits_compute=dp.get_precision('Sale Price')), 'pricelist_id': fields.dummy(string='Pricelist', relation='product.pricelist', type='many2one'), 'name_template': fields.related('product_tmpl_id', 'name', string="Name", type='char', size=128, store=True), } def unlink(self, cr, uid, ids, context=None): unlink_ids = [] unlink_product_tmpl_ids = [] for product in self.browse(cr, uid, ids, context=context): tmpl_id = product.product_tmpl_id.id # Check if the product is last product of this template other_product_ids = self.search(cr, uid, [('product_tmpl_id', '=', tmpl_id), ('id', '!=', product.id)], context=context) if not other_product_ids: unlink_product_tmpl_ids.append(tmpl_id) unlink_ids.append(product.id) self.pool.get('product.template').unlink(cr, uid, unlink_product_tmpl_ids, context=context) return super(product_product, self).unlink(cr, uid, unlink_ids, context=context) def onchange_uom(self, cursor, user, ids, uom_id, uom_po_id): if uom_id and uom_po_id: uom_obj = self.pool.get('product.uom') uom = uom_obj.browse(cursor, user, [uom_id])[0] uom_po = uom_obj.browse(cursor, user, [uom_po_id])[0] if uom.category_id.id != uom_po.category_id.id: return {'value': {'uom_po_id': uom_id}} return False def _check_ean_key(self, cr, uid, ids, context=None): for product in self.browse(cr, uid, ids, context=context): res = check_ean(product.ean13) return res _constraints = [(_check_ean_key, 'Error: Invalid ean code', ['ean13'])] def on_order(self, cr, uid, ids, orderline, quantity): pass def name_get(self, cr, user, ids, context=None): if context is None: context = {} if not len(ids): return [] def _name_get(d): name = d.get('name', '') code = d.get('default_code', False) if code: name = '[%s] %s' % (code, name) if d.get('variants'): name = name + ' - %s' % (d['variants'], ) return (d['id'], name) partner_id = context.get('partner_id', False) result = [] for product in self.browse(cr, user, ids, context=context): sellers = filter(lambda x: x.name.id == partner_id, product.seller_ids) if sellers: for s in sellers: mydict = { 'id': product.id, 'name': s.product_name or product.name, 'default_code': s.product_code or product.default_code, 'variants': product.variants } result.append(_name_get(mydict)) else: mydict = { 'id': product.id, 'name': product.name, 'default_code': product.default_code, 'variants': product.variants } result.append(_name_get(mydict)) return result def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100): if not args: args = [] if name: ids = self.search(cr, user, [('default_code', '=', name)] + args, limit=limit, context=context) if not len(ids): ids = self.search(cr, user, [('ean13', '=', name)] + args, limit=limit, context=context) if not len(ids): ids = self.search(cr, user, [ '|', ('name', operator, name), ('default_code', operator, name) ] + args, limit=limit, context=context) if not len(ids): ptrn = re.compile('(\[(.*?)\])') res = ptrn.search(name) if res: ids = self.search( cr, user, [('default_code', '=', res.group(2))] + args, limit=limit, context=context) else: ids = self.search(cr, user, args, limit=limit, context=context) result = self.name_get(cr, user, ids, context=context) return result # # Could be overrided for variants matrices prices # def price_get(self, cr, uid, ids, ptype='list_price', context=None): if context is None: context = {} if 'currency_id' in context: pricetype_obj = self.pool.get('product.price.type') price_type_id = pricetype_obj.search(cr, uid, [('field', '=', ptype)])[0] price_type_currency_id = pricetype_obj.browse( cr, uid, price_type_id).currency_id.id res = {} product_uom_obj = self.pool.get('product.uom') for product in self.browse(cr, uid, ids, context=context): res[product.id] = product[ptype] or 0.0 if ptype == 'list_price': res[product.id] = (res[product.id] * (product.price_margin or 1.0)) + \ product.price_extra if 'uom' in context: uom = product.uos_id or product.uom_id res[product.id] = product_uom_obj._compute_price( cr, uid, uom.id, res[product.id], context['uom']) # Convert from price_type currency to asked one if 'currency_id' in context: # Take the price_type currency from the product field # This is right cause a field cannot be in more than one currency res[product.id] = self.pool.get('res.currency').compute( cr, uid, price_type_currency_id, context['currency_id'], res[product.id], context=context) return res def copy(self, cr, uid, id, default=None, context=None): if context is None: context = {} product = self.read(cr, uid, id, ['name'], context=context) if not default: default = {} default = default.copy() default['name'] = product['name'] + _(' (copy)') if context.get('variant', False): fields = [ 'product_tmpl_id', 'active', 'variants', 'default_code', 'price_margin', 'price_extra' ] data = self.read(cr, uid, id, fields=fields, context=context) for f in fields: if f in default: data[f] = default[f] data['product_tmpl_id'] = data.get('product_tmpl_id', False) \ and data['product_tmpl_id'][0] del data['id'] return self.create(cr, uid, data) else: return super(product_product, self).copy(cr, uid, id, default=default, context=context)
class account_invoice(osv.osv): _name = "account.invoice" _inherit = "account.invoice" _description = 'Invoice' def _amount_all(self, cr, uid, ids, name, args, context=None): res = {} for invoice in self.browse(cr, uid, ids, context=context): res[invoice.id] = { 'amount_untaxed': 0.0, 'amount_tax': 0.0, 'amount_total': 0.0, 'add_disc_amt': 0.0, 'amount_net': 0.0, } for line in invoice.invoice_line: res[invoice.id]['amount_untaxed'] += line.price_subtotal res[invoice.id]['add_disc_amt'] = res[ invoice.id]['amount_untaxed'] * invoice.add_disc / 100 for line in invoice.tax_line: res[invoice.id]['amount_tax'] += line.amount res[invoice.id]['amount_net'] = res[ invoice.id]['amount_untaxed'] - res[invoice.id]['add_disc_amt'] res[invoice.id]['amount_total'] = res[ invoice.id]['amount_tax'] + res[invoice.id]['amount_net'] return res _columns = { 'add_disc': fields.float('Additional Discount(%)', digits=(4, 2), readonly=True, states={'draft': [('readonly', False)]}), 'add_disc_amt': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Sale Price'), string='Additional Disc Amt', store=True, multi='sums', help="The additional discount on untaxed amount."), 'amount_net': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Sale Price'), string='Net Amount', store=True, multi='sums', help="The amount after additional discount."), 'amount_untaxed': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Account'), string='Untaxed', store=True, multi='all'), 'amount_tax': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Account'), string='Tax', store=True, multi='all'), 'amount_total': fields.function(_amount_all, method=True, digits_compute=dp.get_precision('Account'), string='Total', store=True, multi='all'), } _defaults = { 'add_disc': 0.0, }
[('draft', 'Draft'), ('done', 'Done'), ('load', 'Load')], string='State', required=True, readonly=True), 'limit': fields.integer( 'Limit records', readonly=True, states={'draft': [('readonly', False)]}, help="limit the profit query to this number of record (0 = all)"), 'name': fields.char( 'Name', size=64, required=False, readonly=False), 'date_start': fields.date( 'From', required=False, select=True), 'date_end': fields.date( 'To', required=False, select=True), 'est_util': fields.boolean( 'Estimate utls'), 'force_months': fields.integer( 'Force months qty', digits_compute=dp.get_precision('Account'), help="Set months to be projected (0=auto)"), } _defaults = { 'state': lambda *a: 'draft', 'est_util': lambda *a: True, } _sql_constraints = [ ] ##------------------------------------------------------------------------- ##---------------------------------------------------------- public methods
class _base_stock_picking(object): def onchange_logis_company(self, cr, uid, ids, logistic_company_id, context=None): company_code = '' if logistic_company_id: logistic_company_obj = self.pool.get('logistic.company') company_code = logistic_company_obj.read( cr, uid, logistic_company_id, ['ship_company_code'], context=context)['ship_company_code'] res = {'value': {'ship_company_code': company_code}} return res def init(self, cr): cr.execute( 'alter table stock_picking alter column tot_ship_weight type numeric(16,3)' ) def copy(self, cr, uid, id, default=None, context=None): if default is None: default = {} default['ship_state'] = 'draft' res = super(stock_picking, self).copy(cr, uid, id, default, context=context) return res def distribute_weight(self, cr, uid, ids, context=None): pack_ids_list = self.read(cr, uid, ids, ['packages_ids', 'tot_del_order_weight'], context=context) for pack_ids in pack_ids_list: if pack_ids['tot_del_order_weight'] and pack_ids['packages_ids']: avg_weight = pack_ids['tot_del_order_weight'] / len( pack_ids['packages_ids']) self.pool.get('stock.packages').write(cr, uid, pack_ids['packages_ids'], {'weight': avg_weight}, context=context) return True def _total_weight_net(self, cr, uid, ids, field_name, arg, context=None): """Compute the total net weight of the given Delivery order.""" result = {} for pick in self.browse(cr, uid, ids, context=context): result[pick.id] = 0.0 for line in pick.packages_ids: if line.weight: result[pick.id] += line.weight return result def _total_ord_weight_net(self, cr, uid, ids, field_name, arg, context=None): """Compute the total net weight of the given Delivery order.""" result = {} for pick in self.browse(cr, uid, ids, context=context): result[pick.id] = 0.0 for line in pick.move_lines: if line.product_id: result[pick. id] += line.product_qty * line.product_id.weight_net return result def _get_move_order(self, cr, uid, ids, context=None): """Get the picking ids of the given Stock Moves.""" result = {} for line in self.pool.get('stock.move').browse(cr, uid, ids, context=context): result[line.picking_id.id] = True return result.keys() def _get_order(self, cr, uid, ids, context=None): """Get the picking ids of the given Stock Packages.""" result = {} for line in self.pool.get('stock.packages').browse(cr, uid, ids, context=None): result[line.pick_id.id] = True return result.keys() def _get_company_code(self, cr, user, context=None): return [] _columns = { 'logis_company': fields.many2one( 'logistic.company', 'Logistics Company', help='Name of the Logistics company providing the shipper services.' ), 'freight': fields.boolean( 'Shipment', help='Indicates if the shipment is a freight shipment.'), 'sat_delivery': fields.boolean( 'Saturday Delivery', help='Indicates is it is appropriate to send delivery on Saturday.' ), 'package_type': fields.selection([('01', 'Letter'), ('02', 'Customer Supplied Package'), ('03', 'Tube'), ('04', 'PAK'), ('21', 'ExpressBox'), ('24', '25KG Box'), ('25', '10KG Box'), ('30', 'Pallet'), ('2a', 'Small Express Box'), ('2b', 'Medium Express Box'), ('2c', 'Large Express Box')], 'Package Type', help='Indicates the type of package'), 'bill_shipping': fields.selection([('shipper', 'Shipper'), ('receiver', 'Receiver'), ('thirdparty', 'Third Party')], 'Bill Shipping to', help='Shipper, Receiver, or Third Party.'), 'with_ret_service': fields.boolean( 'With Return Services', help='Include Return Shipping Information in the package.'), 'tot_ship_weight': fields.function( _total_weight_net, method=True, type='float', digits=(16, 3), string='Total Shipment Weight', store={ 'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['packages_ids'], -10), 'stock.packages': (_get_order, ['weight'], -10), }, help= "Adds the Total Weight of all the packages in the Packages Table.", ), 'tot_del_order_weight': fields.function( _total_ord_weight_net, method=True, readonly=True, string='Total Order Weight', store=False, help= "Adds the Total Weight of all the packages in the Packages Table." ), 'packages_ids': fields.one2many("stock.packages", 'pick_id', 'Packages Table'), 'ship_state': fields.selection([('draft', 'Draft'), ('in_process', 'In Process'), ('ready_pick', 'Ready for Pickup'), ('shipped', 'Shipped'), ('delivered', 'Delivered'), ('void', 'Void'), ('hold', 'Hold'), ('cancelled', 'Cancelled')], 'Shipping Status', readonly=True, help='The current status of the shipment'), 'trade_mark': fields.text('Trademarks AREA'), 'ship_message': fields.text('Message'), 'address_validate': fields.selection([('validate', 'Validate'), ('nonvalidate', 'No Validation')], 'Address Validation', help=''' No Validation = No address validation. Validate = Fail on failed address validation. Defaults to validate. Note: Full address validation is not performed. Therefore, it is the responsibility of the Shipping Tool User to ensure the address entered is correct to avoid an address correction fee.''' ), 'ship_description': fields.text('Description'), 'ship_from': fields.boolean( 'Ship From', help= 'Required if pickup location is different from the shipper\'s address..' ), 'ship_from_tax_id_no': fields.char('Identification Number', size=30, select=1), 'shipcharge': fields.float('Shipping Cost', readonly=True), 'ship_from_address': fields.many2one('res.partner', 'Ship From Address', size=30), # 'address': fields.many2one('res.partner', 'Ship From Address'), 'tot_order_weight': fields.related('sale_id', 'total_weight_net', type='float', relation='sale.order', string='Total Order Weight'), 'comm_inv': fields.boolean('Commercial Invoice'), 'cer_orig': fields.boolean('U.S. Certificate of Origin'), 'nafta_cer_orig': fields.boolean('NAFTA Certificate of Origin'), 'sed': fields.boolean('Shipper Export Declaration (SED)'), 'prod_option': fields.selection([('01', 'AVAILABLE TO CUSTOMS UPON REQUEST'), ('02', 'SAME AS EXPORTER'), ('03', 'ATTACHED LIST'), ('04', 'UNKNOWN'), (' ', ' ')], 'Option'), 'prod_company': fields.char( 'CompanyName', size=256, help='Only applicable when producer option is empty or not present.' ), 'prod_tax_id_no': fields.char( 'TaxIdentificationNumber', size=256, help='Only applicable when producer option is empty or not present.' ), 'prod_address_id': fields.many2one( 'res.partner', 'Producer Address', help='Only applicable when producer option is empty or not present.' ), 'inv_option': fields.selection([('01', 'Unknown'), ('02', 'Various'), (' ', ' ')], 'Sold to Option'), 'inv_company': fields.char( 'CompanyName', size=256, help='Only applicable when Sold to option is empty or not present.' ), 'inv_tax_id_no': fields.char( 'TaxIdentificationNumber', size=256, help='Only applicable when Sold to option is empty or not present.' ), 'inv_att_name': fields.char( 'AttentionName', size=256, help='Only applicable when Sold to option is empty or not present.' ), 'inv_address_id': fields.many2one( 'res.partner', 'Sold To Address', help='Only applicable when Sold to option is empty or not present.' ), 'blanket_begin_date': fields.date('Blanket Begin Date'), 'blanket_end_date': fields.date('Blanket End Date'), 'comm_code': fields.char( 'Commodity Code', size=256, ), 'exp_carrier': fields.char('ExportingCarrier', size=256), 'ship_company_code': fields.selection(_get_company_code, 'Ship Company', method=True, size=64), 'ship_charge': fields.float('Value', digits_compute=dp.get_precision('Account')) } _defaults = { 'address_validate': 'nonvalidate', 'comm_inv': False, 'cer_orig': False, 'nafta_cer_orig': False, 'sed': False, 'ship_state': 'draft', 'bill_shipping': 'shipper', 'ship_charge': 0.0 } def process_ship(self, cr, uid, ids, context=None): return True def print_labels(self, cr, uid, ids, context=None): if not ids: return [] return { 'type': 'ir.actions.report.xml', 'report_name': 'multiple.label.print', 'datas': { 'model': 'stock.picking', 'id': ids and ids[0] or False, 'ids': ids, 'report_type': 'pdf' }, 'nodestroy': True } def print_packing_slips(self, cr, uid, ids, context=None): if not ids: return [] packages_ids = [] for package in self.browse(cr, uid, ids[0]).packages_ids: packages_ids.append(package.id) return { 'type': 'ir.actions.report.xml', 'report_name': 'package.packing.slip.print', 'datas': { 'model': 'stock.packages', 'id': ids and ids[0] or False, 'ids': packages_ids, 'report_type': 'pdf' }, 'nodestroy': True } def process_void(self, cr, uid, ids, context=None): picking_pool = self.pool.get('stock.picking') packages_pool = self.pool.get('stock.packages') return packages_pool.cancel_postage( cr, uid, [ package.id for order in picking_pool.browse(cr, uid, ids) for package in order.packages_ids ], context=context) def _get_account_analytic_invoice(self, cursor, user, picking, move_line): partner_id = picking.partner_id and picking.partner_id.id or False analytic_obj = self.pool.get('account.analytic.default') rec = analytic_obj.account_get(cursor, user, move_line.product_id.id, partner_id, user, time.strftime('%Y-%m-%d'), context={}) if rec: return rec.analytic_id.id return super(stock_picking, self)._get_account_analytic_invoice( cursor, user, picking, move_line) def send_conf_mail(self, cr, uid, ids, context=None): for id in ids: obj = self.browse(cr, uid, id) if obj and obj.address_id and obj.address_id.email: email_temp_obj = self.pool.get('email.template') template_id = email_temp_obj.search( cr, uid, [('object_name.model', '=', 'stock.picking'), ('ship_mail', '=', True)], context=context) if template_id: template_obj_list = email_temp_obj.browse( cr, uid, template_id) for template_obj in template_obj_list: subj = self.get_value(obj, template_obj, 'def_subject') or '' vals = { 'email_to': self.get_value(cr, uid, obj, template_obj.def_to, context) or '', 'body_text': self.get_value(cr, uid, obj, template_obj.def_body_text) or '', 'body_html': self.get_value(cr, uid, obj, template_obj.def_body_html) or '', 'account_id': template_obj.from_account.id, 'folder': 'outbox', 'state': 'na', 'subject': self.get_value(cr, uid, obj, template_obj.def_subject) or '', 'email_cc': self.get_value(cr, uid, obj, template_obj.def_cc) or '', 'email_bcc': self.get_value(cr, uid, obj, template_obj.def_bcc) or '', 'email_from': template_obj.from_account.email_id or '', 'reply_to': self.get_value(cr, uid, obj, template_obj.reply_to) or '', 'date_mail': time.strftime('%Y-%m-%d %H:%M:%S'), } if vals['email_to'] and vals['account_id']: mail_id = self.pool.get( 'email_template.mailbox').create( cr, uid, vals, context=context) data = {} data['model'] = 'stock.picking' if template_obj.report_template: reportname = 'report.' + self.pool.get( 'ir.actions.report.xml').read( cr, uid, template_obj.report_template.id, ['report_name'], context=context)['report_name'] service = netsvc.LocalService(reportname) (result, format) = service.create(cr, uid, [id], data, context=context) email_temp_obj._add_attachment( cr, uid, mail_id, subj, base64.b64encode(result), template_obj.file_name or 'Order.pdf', context=context) return True
def split(self, cr, uid, ids, move_ids, context=None): #~ """ To split stock moves into production lot #~ @param self: The object pointer. #~ @param cr: A database cursor #~ @param uid: ID of the user currently logged in #~ @param ids: the ID or list of IDs if we want more than one #~ @param move_ids: the ID or list of IDs of stock move we want to split #~ @param context: A standard dictionary #~ @return: #~ """ check_aux = True if context is None: context = {} inventory_id = context.get('inventory_id', False) prodlot_obj = self.pool.get('stock.production.lot') inventory_obj = self.pool.get('stock.inventory') move_obj = self.pool.get('stock.move') new_move = [] for data in self.browse(cr, uid, ids, context=context): check = self.check_serial(cr, uid, ids, data, context=context) if check: raise osv.except_osv(_("User Error"), _( "These serial has already been used \n%s") % check) for move in move_obj.browse(cr, uid, move_ids, context=context): move_qty = move.product_qty quantity_rest = move.product_qty uos_qty_rest = move.product_uos_qty new_move = [] total_move_qty = 0.0 psm = data.psm if psm: lines = psm.split('\n') lines = list(set(lines)) if '' in lines: lines.remove('') else: lines = [] for line in lines: if move.product_id.track_serial_incoming and move.product_id.track_serial_outgoing and move.picking_id.type == 'out': spl_ids = prodlot_obj.search(cr, uid, [( 'product_id', '=', move.product_id.id), ('name', '=', line)]) if len(spl_ids) < 1: raise osv.except_osv(_('Error !'), _( 'This serial %s is not exist') % line) if move.picking_id.type == "in": self.track_serial_incoming( cr, uid, ids, data.product_id.id, lines, context=context) check_aux = False for line in lines: quantity = 1 total_move_qty += quantity if total_move_qty > move_qty: precision = '%0.' + str(dp.get_precision( 'Product UoM')(cr)[1] or 0) + 'f' raise osv.except_osv(_('Processing Error'), _('Processing quantity %s for %s is larger than the available quantity %s!') % (precision % total_move_qty, move.product_id.name, precision % move_qty)) if quantity <= 0 or move_qty == 0: continue quantity_rest -= quantity uos_qty = quantity / move_qty * move.product_uos_qty uos_qty_rest = quantity_rest / \ move_qty * move.product_uos_qty if quantity_rest < 0: quantity_rest = quantity break default_val = { 'product_qty': quantity, 'product_uos_qty': uos_qty, 'state': move.state } if quantity_rest > 0: current_move = move_obj.copy( cr, uid, move.id, default_val, context=context) if inventory_id and current_move: inventory_obj.write(cr, uid, inventory_id, { 'move_ids': [(4, current_move)]}, context=context) new_move.append(current_move) if quantity_rest == 0: current_move = move.id picking = self.pool.get('stock.move').browse(cr, uid, context.get( 'active_ids'), context=context)[0].picking_id if picking.type == 'out': spl_id = prodlot_obj.search(cr, uid, [( 'product_id', '=', move.product_id.id), ('name', '=', line)]) if spl_id: prodlot_obj.write(cr, uid, [spl_id], {'check_serial': True, 'ref': self.pool.get( 'ir.sequence').get(cr, uid, 'psm.stock.production.lot')+':'+picking.name}) prodlot_brw = prodlot_obj.browse( cr, uid, [spl_id], context=context)[0] if not prodlot_brw.check_serial: prodlot_id = prodlot_brw.id else: raise osv.except_osv(_('Error !'), _( 'These serial already by used in other outgoing picking %s ') % '\n'.join(res)) else: prodlot_id = False if not prodlot_id: picking = self.pool.get('stock.move').browse(cr, uid, context.get( 'active_ids'), context=context)[0].picking_id prodlot_id = prodlot_obj.create(cr, uid, { 'name': line, 'product_id': move.product_id.id, 'ref': self.pool.get('ir.sequence').get(cr, uid, 'psm.stock.production.lot')+':'+picking.name, 'check_serial': check_aux, 'company_id': picking.company_id.id, }, context=context) else: prodlot_id = False if not prodlot_id: picking = self.pool.get('stock.move').browse(cr, uid, context.get( 'active_ids'), context=context)[0].picking_id prodlot_id = prodlot_obj.create(cr, uid, { 'name': line, 'product_id': move.product_id.id, 'ref': self.pool.get('ir.sequence').get(cr, uid, 'psm.stock.production.lot')+':'+picking.name, 'check_serial': check_aux, 'company_id': picking.company_id.id, }, context=context) move_obj.write(cr, uid, [current_move], { 'prodlot_id': prodlot_id, 'state': move.state}) update_val = {} if quantity_rest > 0: update_val['product_qty'] = quantity_rest update_val['product_uos_qty'] = uos_qty_rest update_val['state'] = move.state move_obj.write(cr, uid, [move.id], update_val) return new_move
class change_standard_price(osv.osv_memory): _name = "stock.change.standard.price" _description = "Change Standard Price" _columns = { 'new_price': fields.float( 'Price', required=True, digits_compute=dp.get_precision('Account'), help= "If cost price is increased, stock variation account will be debited " "and stock output account will be credited with the value = (difference of amount * quantity available).\n" "If cost price is decreased, stock variation account will be creadited and stock input account will be debited." ), 'stock_account_input': fields.many2one('account.account', 'Stock Input Account'), 'stock_account_output': fields.many2one('account.account', 'Stock Output Account'), 'stock_journal': fields.many2one('account.journal', 'Stock journal', required=True), 'enable_stock_in_out_acc': fields.boolean('Enable Related Account', ), } def default_get(self, cr, uid, fields, context=None): """ To get default values for the object. @param self: The object pointer. @param cr: A database cursor @param uid: ID of the user currently logged in @param fields: List of fields for which we want default values @param context: A standard dictionary @return: A dictionary which of fields with values. """ if context is None: context = {} product_pool = self.pool.get('product.product') product_obj = product_pool.browse(cr, uid, context.get('active_id', False)) res = super(change_standard_price, self).default_get(cr, uid, fields, context=context) accounts = product_pool.get_product_accounts(cr, uid, context.get( 'active_id', False), context={}) price = product_obj.standard_price if 'new_price' in fields: res.update({'new_price': price}) if 'stock_account_input' in fields: res.update( {'stock_account_input': accounts['stock_account_input']}) if 'stock_account_output' in fields: res.update( {'stock_account_output': accounts['stock_account_output']}) if 'stock_journal' in fields: res.update({'stock_journal': accounts['stock_journal']}) if 'enable_stock_in_out_acc' in fields: res.update({'enable_stock_in_out_acc': True}) return res # onchange_price function is not used anywhere def onchange_price(self, cr, uid, ids, new_price, context=None): """ Sets stock input and output account according to the difference of old price and new price. @param self: The object pointer. @param cr: A database cursor @param uid: ID of the user currently logged in @param ids: List of IDs selected @param new_price: Changed price @param context: A standard dictionary @return: Dictionary of values """ if context is None: context = {} product_obj = self.pool.get('product.product').browse(cr, uid, context.get( 'active_id', False), context=context) price = product_obj.standard_price diff = price - new_price if diff > 0: return {'value': {'enable_stock_in_out_acc': True}} else: return {'value': {'enable_stock_in_out_acc': False}} def change_price(self, cr, uid, ids, context=None): """ Changes the Standard Price of Product. And creates an account move accordingly. @param self: The object pointer. @param cr: A database cursor @param uid: ID of the user currently logged in @param ids: List of IDs selected @param context: A standard dictionary @return: """ if context is None: context = {} rec_id = context and context.get('active_id', False) assert rec_id, _('Active ID is not set in Context') prod_obj = self.pool.get('product.product') res = self.browse(cr, uid, ids, context=context) datas = { 'new_price': res[0].new_price, 'stock_output_account': res[0].stock_account_output.id, 'stock_input_account': res[0].stock_account_input.id, 'stock_journal': res[0].stock_journal.id } prod_obj.do_change_standard_price(cr, uid, [rec_id], datas, context) return {'type': 'ir.actions.act_window_close'}
class change_production_qty(osv.osv_memory): _name = 'change.production.qty' _description = 'Change Quantity of Products' _columns = { 'product_qty': fields.float('Product Qty', digits_compute=dp.get_precision('Product UoM'), required=True), } def default_get(self, cr, uid, fields, context=None): """ To get default values for the object. @param self: The object pointer. @param cr: A database cursor @param uid: ID of the user currently logged in @param fields: List of fields for which we want default values @param context: A standard dictionary @return: A dictionary which of fields with values. """ if context is None: context = {} res = super(change_production_qty, self).default_get(cr, uid, fields, context=context) prod_obj = self.pool.get('mrp.production') prod = prod_obj.browse(cr, uid, context.get('active_id'), context=context) if 'product_qty' in fields: res.update({'product_qty': prod.product_qty}) return res def _update_product_to_produce(self, cr, uid, prod, qty, context=None): move_lines_obj = self.pool.get('stock.move') for m in prod.move_created_ids: move_lines_obj.write(cr, uid, [m.id], {'product_qty': qty}) def change_prod_qty(self, cr, uid, ids, context=None): """ Changes the Quantity of Product. @param self: The object pointer. @param cr: A database cursor @param uid: ID of the user currently logged in @param ids: List of IDs selected @param context: A standard dictionary @return: """ record_id = context and context.get('active_id', False) assert record_id, _('Active Id is not found') prod_obj = self.pool.get('mrp.production') bom_obj = self.pool.get('mrp.bom') for wiz_qty in self.browse(cr, uid, ids, context=context): prod = prod_obj.browse(cr, uid, record_id, context=context) prod_obj.write(cr, uid, prod.id, {'product_qty': wiz_qty.product_qty}) prod_obj.action_compute(cr, uid, [prod.id]) move_lines = prod.move_lines move_lines.extend(prod.picking_id.move_lines) move_lines_obj = self.pool.get('stock.move') for move in move_lines: bom_point = prod.bom_id bom_id = prod.bom_id.id if not bom_point: bom_id = bom_obj._bom_find(cr, uid, prod.product_id.id, prod.product_uom.id) if not bom_id: raise osv.except_osv( _('Error'), _("Couldn't find bill of material for product")) prod_obj.write(cr, uid, [prod.id], {'bom_id': bom_id}) bom_point = bom_obj.browse(cr, uid, [bom_id])[0] if not bom_id: raise osv.except_osv( _('Error'), _("Couldn't find bill of material for product")) factor = prod.product_qty * prod.product_uom.factor / bom_point.product_uom.factor res = bom_obj._bom_explode(cr, uid, bom_point, factor / bom_point.product_qty, []) for r in res[0]: if r['product_id'] == move.product_id.id: move_lines_obj.write(cr, uid, [move.id], {'product_qty': r['product_qty']}) self._update_product_to_produce(cr, uid, prod, wiz_qty.product_qty, context=context) return {}
import time from osv import osv, fields import decimal_precision as dp FISCAL_RULE_COLUMNS = { 'partner_fiscal_type_id': fields.many2one('l10n_br_account.partner.fiscal.type', 'Tipo Fiscal do Parceiro'), 'fiscal_operation_category_id': fields.many2one('l10n_br_account.fiscal.operation.category', 'Categoria', requeried=True), 'fiscal_type': fields.selection([('1', 'Simples Nacional'), ('2', 'Simples Nacional – excesso de sublimite de receita bruta'), ('3', 'Regime Normal')], 'Regime Tributário', required=True), 'revenue_start': fields.float('Faturamento Inicial', digits_compute=dp.get_precision('Account'), help="Faixa inicial de faturamento bruto"), 'revenue_end': fields.float('Faturamento Final', digits_compute=dp.get_precision('Account'), help="Faixa inicial de faturamento bruto"),} FISCAL_RULE_DEFAULTS = { 'fiscal_type': '3', 'revenue_start': 0.00, 'revenue_end': 0.00,} class account_fiscal_position_rule_template(osv.osv): _inherit = 'account.fiscal.position.rule.template' _columns = FISCAL_RULE_COLUMNS _defaults = FISCAL_RULE_DEFAULTS
def check_partite(self,cr,uid,ids,context=False): res = False if ids: for move in self.browse(cr,uid,ids): if move.flag_partite=='C': #deve creare una partita for move_line in move.line_id: conto = False # import pdb;pdb.set_trace() if (not move_line.partita_id) and (not move_line.par_saldi): if move.flag_cliente: if move_line.partner_id.property_account_receivable: conto = move_line.partner_id.property_account_receivable.id else: raise osv.except_osv(_('Errore !'), _('Partita Non Creata Per assenza del Pagamento sulla riga del Partner ' + move_line.partner_id.ref )) if move.flag_fornitore: conto = move_line.partner_id.property_account_payable.id if conto == move_line.account_id.id: # è la riga del cliente if move_line.pagamento_id: importo = float(abs(move_line.credit-move_line.debit)) testa = { 'riga_reg_pnt':move_line.id, 'reg_pnt':move.id, 'num_reg': move.ref, 'data_reg': move.date, 'tipo_documento': move.tipo_documento, 'numero_doc':move.numero_doc, 'data_doc':move.data_doc, 'partner_id': move_line.partner_id.id, 'pagamento_id':move_line.pagamento_id.id, 'totale_partita':arrot(cr,uid,importo,dp.get_precision('Account')), } id_testa_par = self.pool.get('account.partite').create(cr,uid,testa) scadenze = self.pool.get('account.payment.term').compute(cr, uid, move_line.pagamento_id.id,importo, move.data_doc, context) if scadenze: for scadenza in scadenze: riga_scad = { 'name':id_testa_par, 'data_scadenza':scadenza[0], 'importo':arrot(cr,uid,scadenza[1],dp.get_precision('Account')), } res = self.pool.get('account.partite_scadenze').create(cr, uid, riga_scad) #import pdb;pdb.set_trace() ok = self.pool.get('account.partite').write(cr,uid,id_testa_par,testa) ok = self.pool.get('account.move.line').write(cr,uid,[move_line.id],{'partita_id':id_testa_par}) else: raise osv.except_osv(_('Errore !'), _('Partita Non Creata Per assenza del Pagamento sulla riga del Partner')) if move.flag_partite=='S': # TO DO deve lanciare un wizard per saldare ci proviamo a lanciare una action e vediamo #import pdb;pdb.set_trace() #raise osv.except_osv(_('Errore !'), _('Non è Possibile Saldare le Partite Collegate Usare la Funzione SaldaConto')) #return True pass # return { # 'name': 'Saldaconto Partner', # 'view_type': 'form', # 'view_mode': 'form', # 'res_model': 'salda.partite', # 'type': 'ir.actions.act_window', # 'target': 'new', # 'context': context # } return res
def get_out(self, cr, uid, ids, context=None): """ Create the entries in the CashBox . @param self: The object pointer. @param cr: A database cursor @param uid: ID of the user currently logged in @param context: A standard dictionary @return :Return of operation of product """ vals = {} statement_obj = self.pool.get('account.bank.statement') statement_line_obj = self.pool.get('account.bank.statement.line') product_obj = self.pool.get('product.template') productp_obj = self.pool.get('product.product') res_obj = self.pool.get('res.users') for data in self.read(cr, uid, ids, context=context): curr_company = res_obj.browse(cr, uid, uid, context=context).company_id.id statement_id = statement_obj.search(cr, uid, [('journal_id', '=', data['journal_id']), ('company_id', '=', curr_company), ('user_id', '=', uid), ('state', '=', 'open')], context=context) monday = (datetime.today() + relativedelta(weekday=0)).strftime('%Y-%m-%d') sunday = (datetime.today() + relativedelta(weekday=6)).strftime('%Y-%m-%d') done_statmt = statement_obj.search(cr, uid, [('date', '>=', monday+' 00:00:00'), ('date', '<=', sunday+' 23:59:59'), ('journal_id', '=', data['journal_id']), ('company_id', '=', curr_company), ('user_id', '=', uid)], context=context) stat_done = statement_obj.browse(cr, uid, done_statmt, context=context) address_u = res_obj.browse(cr, uid, uid, context=context).address_id am = 0.0 amount_check = productp_obj.browse(cr, uid, data['product_id'], context=context).am_out or False for st in stat_done: for s in st.line_ids: if address_u and s.partner_id == address_u.partner_id and s.am_out: am += s.amount if (-data['amount'] or 0.0) + am < -(res_obj.browse(cr, uid, uid, context=context).company_id.max_diff or 0.0) and amount_check: val = (res_obj.browse(cr, uid, uid).company_id.max_diff or 0.0) + am precision = '%0.' + str(dp.get_precision('Point Of Sale Discount')(cr)[1] or 0) + 'f' raise osv.except_osv(_('Error !'), _('The maximum value you can still withdraw is exceeded. \n Remaining value is equal to %s') % (precision % val,)) acc_id = product_obj.browse(cr, uid, data['product_id'], context=context).property_account_income if not acc_id: raise osv.except_osv(_('Error !'), _('please check that account is set to %s')%(product_obj.browse(cr, uid, data['product_id'], context=context).name)) if not statement_id: raise osv.except_osv(_('Error !'), _('You have to open at least one cashbox')) if statement_id: statement_id = statement_id[0] if not statement_id: statement_id = statement_obj.create(cr, uid, { 'date': time.strftime('%Y-%m-%d 00:00:00'), 'journal_id': data['journal_id'], 'company_id': curr_company, 'user_id': uid, }, context=context) vals['statement_id'] = statement_id vals['journal_id'] = data['journal_id'] if acc_id: vals['account_id'] = acc_id.id amount = data['amount'] or 0.0 if data['amount'] > 0: amount = -data['amount'] vals['amount'] = amount if productp_obj.browse(cr, uid, data['product_id'], context=context).am_out: vals['am_out'] = True vals['ref'] = data['ref'] or '' vals['name'] = "%s: %s " % (product_obj.browse(cr, uid, data['product_id'], context=context).name, data['name'].decode('utf8')) address_u = res_obj.browse(cr, uid, uid, context=context).address_id if address_u: vals['partner_id'] = address_u.partner_id and address_u.partner_id.id or None statement_line_obj.create(cr, uid, vals, context=context) return {}
def split(self, cr, uid, ids, move_ids, context=None): """ To split stock moves into production lot @param self: The object pointer. @param cr: A database cursor @param uid: ID of the user currently logged in @param ids: the ID or list of IDs if we want more than one @param move_ids: the ID or list of IDs of stock move we want to split @param context: A standard dictionary @return: """ if context is None: context = {} inventory_id = context.get('inventory_id', False) prodlot_obj = self.pool.get('stock.production.lot') inventory_obj = self.pool.get('stock.inventory') move_obj = self.pool.get('stock.move') new_move = [] for data in self.browse(cr, uid, ids, context=context): for move in move_obj.browse(cr, uid, move_ids, context=context): move_qty = move.product_qty quantity_rest = move.product_qty uos_qty_rest = move.product_uos_qty new_move = [] if data.use_exist: lines = [l for l in data.line_exist_ids if l] else: lines = [l for l in data.line_ids if l] total_move_qty = 0.0 for line in lines: quantity = line.quantity total_move_qty += quantity if total_move_qty > move_qty: precision = '%0.' + str(dp.get_precision('Product UoM')(cr)[1] or 0) + 'f' raise osv.except_osv(_('Processing Error'), _('Processing quantity %s for %s is larger than the available quantity %s!')\ % (precision % total_move_qty, move.product_id.name, precision % move_qty)) if quantity <= 0 or move_qty == 0: continue quantity_rest -= quantity uos_qty = quantity / move_qty * move.product_uos_qty uos_qty_rest = quantity_rest / move_qty * move.product_uos_qty if quantity_rest < 0: quantity_rest = quantity break default_val = { 'product_qty': quantity, 'product_uos_qty': uos_qty, 'state': move.state } if quantity_rest > 0: current_move = move_obj.copy(cr, uid, move.id, default_val, context=context) if inventory_id and current_move: inventory_obj.write(cr, uid, inventory_id, {'move_ids': [(4, current_move)]}, context=context) new_move.append(current_move) if quantity_rest == 0: current_move = move.id prodlot_id = False if data.use_exist: prodlot_id = line.prodlot_id.id if not prodlot_id: prodlot_id = prodlot_obj.create(cr, uid, { 'name': line.name, 'product_id': move.product_id.id}, context=context) move_obj.write(cr, uid, [current_move], {'prodlot_id': prodlot_id, 'state':move.state}) update_val = {} if quantity_rest > 0: update_val['product_qty'] = quantity_rest update_val['product_uos_qty'] = uos_qty_rest update_val['state'] = move.state move_obj.write(cr, uid, [move.id], update_val) return new_move
def importa(self, cr, uid, ids, context=None): FinoaData = self.browse(cr, uid, ids)[0].a_data_doc Scadobj = self.pool.get("fiscaldoc.scadenze") FatObj = self.pool.get("fiscaldoc.header") # import pdb;pdb.set_trace() filtro1 = [("tipo_documento", "in", ("FA", "FI", "FD"))] idsTipoDoc = self.pool.get("fiscaldoc.causalidoc").search(cr, uid, filtro1) idsTipoDoc = tuple(idsTipoDoc) filtro = [("data_documento", "<=", FinoaData), ("tipo_doc", "in", idsTipoDoc)] idsFat = tuple(FatObj.search(cr, uid, filtro)) # PRENDE TUTTI I DOCUMENTI FINO ALLA DATA INTERESSATA ghfg filtro = [ ("name", "in", idsFat), ("effetto_scadenza_id", "=", ""), ("tipo_scadenza", "=", "RB"), ("generato_effetto", "=", False), ] idsScad = Scadobj.search(cr, uid, filtro) ids_effetti = [] if idsScad: for scad_id in idsScad: Scadenzabrw = Scadobj.browse(cr, uid, [scad_id])[0] if ( not Scadenzabrw.name.partner_id.raggruppa_riba ): # se il partner raggruppa gli effetti ragiona diversamente NumEff = self.pool.get("ir.sequence").get(cr, uid, "effetti") if Scadenzabrw.name.banca_patner.id: TestaEffetto = { "name": NumEff, "data_scadenza": Scadenzabrw.data_scadenza, "cliente_id": Scadenzabrw.name.partner_id.id, "banca_patner": Scadenzabrw.name.banca_patner.id, "note": "Doc.N " + str(Scadenzabrw.name.numdoc) + "Del " + Scadenzabrw.name.data_documento, "importo_effetto": arrot( cr, uid, Scadenzabrw.importo_scadenza, dp.get_precision("Account") ), } idHeadEffetto = self.pool.get("effetti").create(cr, uid, TestaEffetto) ids_effetti.append(idHeadEffetto) RigaEffetto = { "name": idHeadEffetto, "scadenza_id": Scadenzabrw.id, "numero_doc": Scadenzabrw.name.name, "importo_scadenza": Scadenzabrw.importo_scadenza, "data_documento": Scadenzabrw.name.data_documento, "totale_documento": arrot( cr, uid, Scadenzabrw.name.totale_documento, dp.get_precision("Account") ), "pagamento": Scadenzabrw.name.pagamento_id.id, } idRigaScadEff = self.pool.get("effetti.scadenze").create(cr, uid, RigaEffetto) ok = self.pool.get("effetti").write(cr, uid, idHeadEffetto, TestaEffetto) for scad_eff in self.pool.get("effetti").browse(cr, uid, idHeadEffetto).righe_scadenze: ok = Scadobj.write( cr, uid, scad_eff.scadenza_id.id, {"effetto_scadenza_id": scad_eff.id, "generato_effetto": True}, ) else: raise osv.except_osv(_("ERRORE !"), _("Banca Assente sul documento " + Scadenzabrw.name.name)) else: # TO DO deve raggruppare l'effetto su + scadenze filtro = [("data_scadenza", "", Scadenzabrw.data_scadenza), ("cliente_id", "", "isnull")] pass """ Verifica che non esista già un effetto non presentato con la stessa scadenza se non esiste crea l'effetto altrimenti aggiunge solo la scadenza e ne ricaolcola il totale effetto """ pass else: raise osv.except_osv(_("ERRORE !"), _("NON SONO STATI TROVATI EFFETTI DA IMPORTARE")) # return {'type': 'ir.actions.act_window_close'} # non va bene deve aprire la finestra degli effetti return { "name": _("Effetti"), "view_type": "form", "view_mode": "tree,form", "res_model": "effetti", "res_id": ids_effetti, "view_id": False, "context": context, "type": "ir.actions.act_window", }
def scrive_account_move_line(self,cr, uid,move_head,doc, context): def default_riga(move_head,doc): riga = { 'name': move_head.ref, 'period_id':move_head.period_id.id, 'journal_id':move_head.journal_id.id, 'partner_id':doc.partner_id.id, 'move_id':move_head.id, 'date':move_head.date, 'ref':move_head.ref, 'causale_id':move_head.causale_id.id, } return riga def cerca_controp(riga_doc): conto_id = False if riga_doc.contropartita: conto_id= riga_doc.contropartita.id elif riga_doc.product_id.categ_id.property_account_income_categ: conto_id = riga_doc.product_id.categ_id.property_account_income_categ.id return conto_id testo_log = """ """ flag_scritto= True # ora cicla sulle righe documento, ma deve riportarsi gli sconti di testata. # spese diverse ids_controp = self.pool.get('controp.costi.ricavi').search(cr,uid,[]) if ids_controp: controp_obj= self.pool.get('controp.costi.ricavi').browse(cr,uid,ids_controp[0]) else: raise osv.except_osv(_('ERRORE !'), _('NON SONO DEFINITE LE CONTROPARTITE COSTI E RICAVI ')) flag_scritto= False if doc.spese_imballo: riga = default_riga(move_head,doc) if doc.tipo_documento=="NC": segno = "DA" else: segno = "AV" if segno=="DA": #segno dare riga['credit']=0 riga['debit']=doc.spese_imballo else: #segno dare riga['credit']=doc.spese_imballo riga['debit']=0 riga['account_id']= controp_obj.conto_v_sp_imballo.id #import pdb;pdb.set_trace() print "riga spese imballo ", riga id_riga = self.pool.get('account.move.line').create(cr,uid,riga) # RIGA IMBALLO if not id_riga: flag_scritto= False if doc.spese_incasso: riga = default_riga(move_head,doc) if doc.tipo_documento=="NC": segno = "DA" else: segno = "AV" if segno=="DA": #segno dare riga['credit']=0 riga['debit']=doc.spese_incasso else: #segno dare riga['credit']=doc.spese_incasso riga['debit']=0 riga['account_id']= controp_obj.conto_v_sp_incasso.id # import pdb;pdb.set_trace() print "riga spese incasso ", riga id_riga = self.pool.get('account.move.line').create(cr,uid,riga) # RIGA INCASSO if not id_riga: flag_scritto= False if doc.spese_trasporto: riga = default_riga(move_head,doc) if doc.tipo_documento=="NC": segno = "DA" else: segno = "AV" if segno=="DA": #segno dare riga['credit']=0 riga['debit']=doc.spese_trasporto else: #segno dare riga['credit']=doc.spese_trasporto riga['debit']=0 riga['account_id']= controp_obj.conto_v_sp_trasporto.id # import pdb;pdb.set_trace() print "riga spese trasporto ", riga id_riga = self.pool.get('account.move.line').create(cr,uid,riga) # RIGA TRASPORTO if not id_riga: flag_scritto= False # if flag_scritto: # testo_log += " Documento "+ doc.name + " CONTABILIZZATO ALLA REGISTRAZIONE "+ move_obj.name +'\n' riga = default_riga(move_head,doc) riga['pagamento_id']=doc.pagamento_id.id righe={} for riga_art in doc.righe_articoli: if doc.sconto_partner or doc.sconto_pagamento: netto = riga_art.totale_riga if doc.sconto_partner: netto = netto-(netto*doc.sconto_partner/100) netto = arrot(cr,uid,netto,dp.get_precision('Account')) if doc.sconto_pagamento: netto = netto-(netto*doc.sconto_pagamento/100) netto = arrot(cr,uid,netto,dp.get_precision('Account')) else: netto = riga_art.totale_riga conto = cerca_controp(riga_art) if not conto: testo_log += " Documento "+ doc.name + " riga "+ riga_art.product_id.default_code+ ' senza contropartita ricavi '+'DOCUMENTO NON CONTABILIZZATO \n' flag_scritto= False else: if netto==0: pass else: riga= righe.get(conto,False) if not riga: riga = default_riga(move_head,doc) riga['credit']=0 riga['debit']=0 if doc.tipo_documento=="NC": segno = "DA" else: segno = "AV" if segno=="DA": if True :# netto>0: messo in rem per sommare algebricamnete sul totale conto se c'è solo l'importo negativo #segno dare riga['credit']+=0 riga['debit']+=netto else: riga['credit']+=netto*-1 riga['debit']+=0 else: #segno dare if True : #netto>0: messo in rem per sommare algebricamnete sul totale conto si incazzerà se c'è solo l'importo negativo riga['credit']+=netto riga['debit']+=0 else: riga['credit']+=0 riga['debit']+=netto*-1 riga['account_id']= conto #import pdb;pdb.set_trace() righe[conto]=riga if righe: for riga in righe.values(): # import pdb;pdb.set_trace() print "riga ricavo ", riga id_riga = self.pool.get('account.move.line').create(cr,uid,riga) # RIGA RICAVO if not id_riga: flag_scritto= False #cicla sulle righe iva adesso e scrive le stesse for riga_iva in doc.righe_totali_iva: riga = default_riga(move_head,doc) riga['pagamento_id']=doc.pagamento_id.id segno = move_head.causale_id.segno_conto_iva conto = move_head.causale_id.conto_iva.id riga['account_id']=conto riga['imponibile']=riga_iva.imponibile riga['account_tax_id']=riga_iva.codice_iva.id if segno=="DA": #segno dare riga['credit']=0 riga['debit']=riga_iva.imposta else: #segno dare riga['credit']=riga_iva.imposta riga['debit']=0 #import pdb;pdb.set_trace() print "riga iva ", riga id_riga = self.pool.get('account.move.line').create(cr,uid,riga) # RIGA IVA if not id_riga: flag_scritto= False # inizia con lo scrivere i dati del cliente riga = default_riga(move_head,doc) if doc.tipo_documento=="NC": segno = "AV" else: segno = "DA" if segno=="DA": #segno dare riga['credit']=0 riga['debit']=doc.totale_documento else: #segno dare riga['credit']=doc.totale_documento riga['debit']=0 riga['account_id']= doc.partner_id.property_account_receivable.id riga['totdocumento']=doc.totale_documento riga['pagamento_id']=doc.pagamento_id.id #import pdb;pdb.set_trace() print "riga cliente ", riga id_riga = self.pool.get('account.move.line').create(cr,uid,riga) # RIGA CLIENTE if not id_riga: flag_scritto= False #cicla sulle righe iva adesso e scrive le stesse return [testo_log,flag_scritto]
class product_product(orm.Model): """ Inherit Product in order to add an "Bom Stock" field """ class CreateWarehouseProcess(multiprocessing.Process): def __init__(self, cr, uid, ids, field_names, arg, context, return_funct_dict): # Inizializzazione superclasse multiprocessing.Process.__init__(self) self.cr = pooler.get_db(cr.dbname).cursor() self.uid = uid self.ids = ids self.field_names = field_names self.arg = arg self.context = context self.return_funct_dict = return_funct_dict self.product_product_obj = pooler.get_pool( self.cr.dbname).get('product.product') def run(self): try: self.return_funct_dict.update( self.product_product_obj._product_available( self.cr, self.uid, self.ids, self.field_names, self.arg, self.context)) except Exception as e: # Annulla le modifiche fatte _logger.error(u'Error: {error}'.format(error=e)) self.cr.rollback() if not self.cr.closed: self.cr.close() p = psutil.Process(self.pid) p.kill() _logger.info(u'STOP: {error}'.format(error=self)) return True def __del__(self): if not self.cr.closed: self.cr.close() return True class CreateProductCost(multiprocessing.Process): def __init__(self, cr, uid, ids, bom_properties, context, return_funct_dict): # Inizializzazione superclasse multiprocessing.Process.__init__(self) self.cr = pooler.get_db(cr.dbname).cursor() self.uid = uid self.ids = ids self.bom_properties = bom_properties self.context = context self.return_funct_dict = return_funct_dict self.product_product_obj = pooler.get_pool( self.cr.dbname).get('product.product') def run(self): try: for product in self.product_product_obj.browse( self.cr, self.uid, self.ids, context=self.context): self.return_funct_dict[ product. id] = self.product_product_obj._compute_product_purchase_price( self.cr, self.uid, product, self.bom_properties, context=self.context) except Exception as e: # Annulla le modifiche fatte _logger.error(u'Error: {error}'.format(error=e)) self.cr.rollback() if not self.cr.closed: self.cr.close() p = psutil.Process(self.pid) p.kill() _logger.info(u'STOP: {error}'.format(error=self)) return True def __del__(self): _logger.info(u'TERMINATE: {error}'.format(error=self)) if not self.cr.closed: self.cr.close() return True class UpdateCachePrice(threading.Thread): def __init__(self, cr, uid, product_product_obj, split_ids, context=None): self.cr = pooler.get_db(cr.dbname).cursor() self.product_product_obj = product_product_obj self.uid = uid self.context = context self.product_ids = split_ids threading.Thread.__init__(self) def run(self): try: for product in self.product_product_obj.read( self.cr, self.uid, self.product_ids, ['cost_price'], self.context): cost_price = product['cost_price'] return True except Exception as e: # Rollback _logger.error(u'Error: {error}'.format(error=e)) self.cr.rollback() finally: if not self.cr.closed: self.cr.close() return True def terminate(self): if not self.cr.closed: self.cr.close() return True @staticmethod def _chunkIt(seq, size): newseq = [] splitsize = 1.0 / size * len(seq) for line in range(size): newseq.append( seq[int(round(line * splitsize)):int(round((line + 1) * splitsize))]) return newseq _inherit = 'product.product' # def _get_prefered_supplier(self, cr, uid, ids, field_name, args, context): # res = {} # for line in self.browse(cr, uid, ids, context): # res[line.id] = line.seller_ids and line.seller_ids[0].name.id or False # return res def __init__(self, cr, uid): super(product_product, self).__init__(cr, uid) if CACHE_TYPE == 'redis': try: from openerp.addons.core_extended.redis import Redis host = config.get('redis_host', 'localhost') self.product_cost_cache = Redis(host, database=cr.db_name, model=self._name) except: _logger.error("Unable to import Redis") from openerp.addons.core_extended.dict_cache import SimpleCache self.product_cost_cache = SimpleCache() else: from openerp.addons.core_extended.dict_cache import SimpleCache self.product_cost_cache = SimpleCache() def _hook_compute_purchase_price_no_supplier(self, product): return product.standard_price def product_cache(func): def cached_product(self, cr, uid, product, bom_properties, context=None): if product.id in self.product_cost_cache and not context.get( 'partner_name', False): _logger.debug('Returning from cache') return self.product_cost_cache[product.id] else: value = func(self, cr, uid, product, bom_properties, context=context) self.product_cost_cache[product.id] = value return value if ENABLE_CACHE: return cached_product else: return func @product_cache def _get_subproduct_cost_price(self, cr, uid, product, bom_properties, context=None): return product.cost_price @product_cache def _compute_product_purchase_price(self, cr, uid, product, bom_properties, context=None): context = context or self.pool['res.users'].context_get(cr, uid) bom_properties = bom_properties or [] user = self.pool['res.users'].browse(cr, uid, uid, context) bom_obj = self.pool['mrp.bom'] uom_obj = self.pool['product.uom'] debug_logger = True if ENABLE_CACHE: if debug_logger: _logger.debug( u'[{product.default_code}] {product.name}'.format( product=product)) # cached_price = self.product_cost_cache.get(product_id, 0) bom_id = bom_obj._bom_find(cr, uid, product.id, product_uom=None, properties=bom_properties) if bom_id: sub_bom_ids = bom_obj.search(cr, uid, [('bom_id', '=', bom_id)], context=context) sub_products = bom_obj.browse(cr, uid, sub_bom_ids, context) price = 0. if ENABLE_CACHE and debug_logger: _logger.debug( u'[{product.default_code}] Start Explosion ========================' .format(product=product)) for sub_product in sub_products: if sub_product.product_id.id == product.id: error = "Product '{product.name}' (id: {product.id}) is referenced to itself".format( product=product) _logger.error(error) continue # std_price = sub_product.standard_price # if ENABLE_CACHE: # if sub_product.product_id.id in self.product_cost_cache: # std_price = self.product_cost_cache[sub_product.product_id.id] # else: # std_price = sub_product.product_id.cost_price # self.product_cost_cache[sub_product.product_id.id] = std_price # else: # std_price = sub_product.product_id.cost_price std_price = self._get_subproduct_cost_price( cr, uid, sub_product.product_id, False, context) qty = uom_obj._compute_qty( cr, uid, from_uom_id=sub_product.product_uom.id, qty=sub_product.product_qty, to_uom_id=sub_product.product_id.uom_po_id.id) if ENABLE_CACHE and debug_logger: _logger.debug( u'[{product.default_code}] price += {std_price} * {qty}' .format(product=sub_product.product_id, std_price=std_price, qty=qty)) # print(std_price, qty) price += std_price * qty if sub_products: # Don't use browse when we already have it bom = sub_products[0].bom_id else: bom = bom_obj.browse(cr, uid, bom_id, context) if bom.routing_id and not context.get('exclude_routing', False): for wline in bom.routing_id.workcenter_lines: wc = wline.workcenter_id cycle = wline.cycle_nbr # hour = (wc.time_start + wc.time_stop + cycle * wc.time_cycle) * (wc.time_efficiency or 1.0) price += wc.costs_cycle * cycle + wc.costs_hour * wline.hour_nbr price /= bom.product_qty price = uom_obj._compute_price(cr, uid, bom.product_uom.id, price, bom.product_id.uom_id.id) if ENABLE_CACHE and debug_logger: _logger.debug( u'==== SUM [{product.default_code}] bom_price = {price}'. format(product=product, price=price)) return price else: # no BoM: use standard_price # use standard_price if no supplier indicated # if product_id in self.product_cost_cache and ENABLE_CACHE and not context.get('partner_name', False): # return self.product_cost_cache[product_id] if product.prefered_supplier: pricelist = product.prefered_supplier.property_product_pricelist_purchase or False ctx = {'date': time.strftime(DEFAULT_SERVER_DATE_FORMAT)} if context.get('partner_name', False): partner_name = context.get('partner_name') partner_ids = self.pool['res.partner'].search( cr, uid, [('name', '=', partner_name)], context=context) partner_id = partner_ids[0] else: partner_id = False if pricelist: price, rule = self.pool[ 'product.pricelist'].price_rule_get_multi( cr, uid, [pricelist.id], products_by_qty_by_partner=[(product, 1, partner_id)], context=context)[product.id][pricelist.id] else: raise orm.except_orm( _("Error"), _("The supplier {supplier} have no pricelist associated" ).format(supplier=product.prefered_supplier.name)) price_subtotal = 0.0 if pricelist: from_currency = pricelist.currency_id.id to_currency = user.company_id.currency_id.id price_subtotal = self.pool['res.currency'].compute( cr, uid, from_currency_id=from_currency, to_currency_id=to_currency, from_amount=price, context=context) cost_price = price_subtotal or price or product.standard_price else: cost_price = self._hook_compute_purchase_price_no_supplier( product) if ENABLE_CACHE and debug_logger: _logger.debug( u'NO BOM [{product.default_code}] price = {price}'.format( product=product, price=cost_price)) return cost_price def _compute_purchase_price_new(self, cr, uid, ids, product_uom=None, bom_properties=None, context=None): ''' Compute the purchase price, taking into account sub products and routing ''' context = context or self.pool['res.users'].context_get(cr, uid) bom_properties = bom_properties or [] if not ids: ids = self.search(cr, uid, []) workers = multiprocessing.cpu_count() / 2 res = dict.fromkeys(ids, 0.0) with multiprocessing.Manager() as manager: return_funct_dict = manager.dict() threads = [] for product_ids in self._chunkIt(ids, workers): if product_ids: thread = self.CreateProductCost(cr, uid, product_ids, bom_properties, context, return_funct_dict) # thread.daemon = True thread.start() threads.append(thread) # wait for finish all multiprocessing created for job in threads: job.join() for return_funct_dict_key in return_funct_dict.keys(): res[return_funct_dict_key] = return_funct_dict[ return_funct_dict_key] # # for product in self.browse(cr, uid, ids, context): # try: # res[product.id] = self._compute_product_purchase_price(cr, uid, product, bom_properties, # context=context) # except Exception as e: # res[product.id] = 99999999 # _logger.error(u'{product} ERRORE: {error}'.format(product=product.name_get()[0][1], error=e)) return res def get_cost_field(self, cr, uid, ids, context=None): start_time = datetime.now() context = context or self.pool['res.users'].context_get(cr, uid) end_time = datetime.now() duration_seconds = (end_time - start_time) duration = '{sec}'.format(sec=duration_seconds) _logger.info(u'get_cost_field get in {duration} for {id}'.format( duration=duration, id=ids)) res = self._cost_price(cr, uid, ids, '', [], context) return res def _cost_price(self, cr, uid, ids, field_name, arg, context=None): # _logger.error( # u'START _cost_price for {ids} and {field}'.format(ids=ids, field=field_name)) context = context or self.pool['res.users'].context_get(cr, uid) product_uom = context.get('product_uom') bom_properties = context.get('properties') company = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id ctx = context.copy() ctx.update({'exclude_routing': company.exclude_routing}) # Set to False to use old function new = True if new: start_time = datetime.now() res = self._compute_purchase_price_new(cr, uid, ids, product_uom, bom_properties, context=ctx) end_time = datetime.now() duration_seconds = (end_time - start_time) duration = '{sec}'.format(sec=duration_seconds) _logger.info( u'_cost_price_new get in {duration} for {qty} products'.format( duration=duration, qty=len(ids))) # else: # start_time = datetime.now() # res = self._compute_purchase_price(cr, uid, ids, product_uom, bom_properties, context=ctx) # end_time = datetime.now() # duration_seconds = (end_time - start_time) # duration = '{sec}'.format(sec=duration_seconds) # _logger.info(u'_cost_price get in {duration} for {qty} products'.format(duration=duration, qty=len(ids))) return res def _kit_filter(self, cr, uid, obj, name, args, context): context = context or self.pool['res.users'].context_get(cr, uid) if not args: return [] bom_obj = self.pool['mrp.bom'] for search in args: if search[0] == 'is_kit': if search[2]: bom_ids = bom_obj.search(cr, uid, [('bom_id', '=', False)], context=context) if bom_ids: bom_product_ids = self.search( cr, uid, [('bom_ids', 'in', bom_ids)], context=context) # res = [bom.product_id.id for bom in bom_obj.browse(cr, uid, bom_ids, context)] return [('id', 'in', bom_product_ids)] else: return [('id', 'in', [])] return [] def _is_kit(self, cr, uid, ids, product_uom=None, bom_properties=None, context=None): if not len(ids): return [] ''' Show if have or not a bom ''' res = {} ids = ids or [] for product_id in ids: # bom_id = bom_obj._bom_find(cr, uid, product.id, product_uom=None, properties=bom_properties) cr.execute( """SELECT id FROM mrp_bom WHERE product_id={product_id} and bom_id is null""" .format(product_id=product_id)) bom_id = cr.fetchall() if not bom_id: res[product_id] = False else: res[product_id] = True return res """ Inherit Product in order to add an "Bom Stock" field """ def _bom_stock_mapping(self, cr, uid, context=None): return { 'real': 'qty_available', 'virtual': 'virtual_available', 'immediately': 'immediately_usable_qty' } # from profilehooks import profile # @profile(immediate=True) def _compute_bom_stock(self, cr, uid, product, quantities, ref_stock, context=None): context = context or self.pool['res.users'].context_get(cr, uid) bom_obj = self.pool['mrp.bom'] uom_obj = self.pool['product.uom'] mapping = self._bom_stock_mapping(cr, uid, context=context) stock_field = mapping[ref_stock] product_qty = quantities.get(stock_field, 0.0) # find a bom on the product bom_id = bom_obj._bom_find(cr, uid, product.id, product.uom_id.id, properties=[]) if bom_id: prod_min_quantities = [] bom = bom_obj.browse(cr, uid, bom_id, context=context) if bom.bom_lines: stop_compute_bom = False # Compute stock qty of each product used in the BoM and # get the minimal number of items we can produce with them for line in bom.bom_lines: prod_min_quantity = 0.0 bom_qty = line.product_id[ stock_field] # expressed in product UOM # the reference stock of the component must be greater # than the quantity of components required to # build the bom line_product_qty = uom_obj._compute_qty_obj( cr, uid, line.product_uom, line.product_qty, line.product_id.uom_id, context=context) if line_product_qty and (bom_qty > line_product_qty): prod_min_quantity = bom_qty / line_product_qty # line.product_qty is always > 0 else: # if one product has not enough stock, # we do not need to compute next lines # because the final quantity will be 0.0 in any case stop_compute_bom = True prod_min_quantities.append(prod_min_quantity) if stop_compute_bom: break produced_qty = uom_obj._compute_qty_obj(cr, uid, bom.product_uom, bom.product_qty, bom.product_id.uom_id, context=context) if prod_min_quantities: product_qty += min(prod_min_quantities) * produced_qty else: product_qty += produced_qty return product_qty def _product_available_thread(self, cr, uid, ids, field_names=[], arg=False, context=None): # We need available, virtual or immediately usable # quantity which is selected from company to compute Bom stock Value # so we add them in the calculation. context = context or self.pool['res.users'].context_get(cr, uid) start_time = datetime.now() comp_obj = self.pool['res.company'] if 'bom_stock' in field_names: field_names.append('qty_available') field_names.append('immediately_usable_qty') field_names.append('virtual_available') company_id = self.pool['res.users']._get_company(cr, uid, context=context) company = comp_obj.read(cr, uid, company_id, ['exclude_consu_stock', 'ref_stock'], context=context) res = {} for product_id in ids: res[product_id] = {}.fromkeys(field_names, 0.0) if company['exclude_consu_stock']: product_stock_ids = self.search( cr, uid, [('id', 'in', ids), ('type', 'not in', ['consu', 'service'])], context=context) else: product_stock_ids = ids if product_stock_ids: # if product_stock_ids is [] get_product_available on stock search for all product workers = multiprocessing.cpu_count() / 2 with multiprocessing.Manager() as manager: return_funct_dict = manager.dict() threads = [] for split in self._chunkIt(product_stock_ids, workers): if split: thread = self.CreateWarehouseProcess( cr, uid, split, field_names, arg, context, return_funct_dict) thread.daemon = True thread.start() threads.append(thread) # wait for finish all multiprocessing created for job in threads: job.join() for return_funct_dict_key in return_funct_dict.keys(): res[return_funct_dict_key] = return_funct_dict[ return_funct_dict_key] if 'bom_stock' in field_names: for product_id, stock_qty in res.iteritems(): product = self.browse(cr, uid, product_id, context=context) res[product_id]['bom_stock'] = self._compute_bom_stock( cr, uid, product, stock_qty, company['ref_stock'], context=context) end_time = datetime.now() duration_seconds = (end_time - start_time) duration = '{sec}'.format(sec=duration_seconds) _logger.info( u'_product_available get in {duration}'.format(duration=duration)) return res def _get_boms(self, cr, uid, ids, field_name, arg, context): result = {} for product_id in ids: result[product_id] = self.pool['mrp.bom'].search( cr, uid, [('product_id', '=', product_id), ('bom_id', '=', False)], context=context) return result def _get_prefered_supplier(self, cr, uid, ids, field_name, arg, context): result = {} for product in self.browse(cr, uid, ids, context): result[product.id] = product.seller_ids and product.seller_ids[ 0].name.id or False return result def price_get(self, cr, uid, ids, ptype='list_price', context=None): start_time = datetime.now() context = context or self.pool['res.users'].context_get(cr, uid) if 'currency_id' in context: pricetype_obj = self.pool['product.price.type'] price_type_id = pricetype_obj.search(cr, uid, [('field', '=', ptype)], context=context)[0] price_type_currency_id = pricetype_obj.browse( cr, uid, price_type_id, context).currency_id.id res = {} product_uom_obj = self.pool['product.uom'] for product in self.browse(cr, uid, ids, context=context): res[product.id] = product[ptype] or 0.0 if ptype == 'standard_price' and product.is_kit: res[product.id] = product.cost_price or product.standard_price if ptype == 'list_price': res[product.id] = ( res[product.id] * (product.price_margin or 1.0)) + product.price_extra if 'uom' in context: uom = product.uom_id or product.uos_id res[product.id] = product_uom_obj._compute_price( cr, uid, uom.id, res[product.id], context['uom']) # Convert from price_type currency to asked one if 'currency_id' in context: # Take the price_type currency from the product field # This is right cause a field cannot be in more than one currency res[product.id] = self.pool['res.currency'].compute( cr, uid, price_type_currency_id, context['currency_id'], res[product.id], context=context) end_time = datetime.now() duration_seconds = (end_time - start_time) duration = '{sec}'.format(sec=duration_seconds) _logger.info(u'price_get get in {duration}'.format(duration=duration)) return res _columns = { 'date_inventory': fields.function(lambda *a, **k: {}, method=True, type='date', string="Date Inventory"), 'cost_price': fields.function(_cost_price, method=True, string=_('Cost Price (incl. BoM)'), digits_compute=dp.get_precision('Purchase Price'), help="The cost price is the standard price or, if the product has a bom, " "the sum of all standard price of its components. it take also care of the " "bom costing like cost per cylce."), 'prefered_supplier': fields.function(_get_prefered_supplier, type='many2one', relation='res.partner', string='Prefered Supplier'), 'is_kit': fields.function(_is_kit, fnct_search=_kit_filter, method=True, type="boolean", string="Kit"), 'bom_lines': fields.function(_get_boms, relation='mrp.bom', string='Boms', type='one2many', method=True), 'qty_available': fields.function( _product_available_thread, multi='qty_available', type='float', digits_compute=dp.get_precision('Product UoM'), string='Quantity On Hand', help="Current quantity of products.\n" "In a context with a single Stock Location, this includes " "goods stored at this Location, or any of its children.\n" "In a context with a single Warehouse, this includes " "goods stored in the Stock Location of this Warehouse, " "or any " "of its children.\n" "In a context with a single Shop, this includes goods " "stored in the Stock Location of the Warehouse of this Shop, " "or any of its children.\n" "Otherwise, this includes goods stored in any Stock Location " "typed as 'internal'."), 'virtual_available': fields.function( _product_available_thread, multi='qty_available', type='float', digits_compute=dp.get_precision('Product UoM'), string='Quantity Available', help="Forecast quantity (computed as Quantity On Hand " "- Outgoing + Incoming)\n" "In a context with a single Stock Location, this includes " "goods stored at this Location, or any of its children.\n" "In a context with a single Warehouse, this includes " "goods stored in the Stock Location of this Warehouse, " "or any " "of its children.\n" "In a context with a single Shop, this includes goods " "stored in the Stock Location of the Warehouse of this Shop, " "or any of its children.\n" "Otherwise, this includes goods stored in any Stock Location " "typed as 'internal'."), 'incoming_qty': fields.function( _product_available_thread, multi='qty_available', type='float', digits_compute=dp.get_precision('Product UoM'), string='Incoming', help="Quantity of products that are planned to arrive.\n" "In a context with a single Stock Location, this includes " "goods arriving to this Location, or any of its children.\n" "In a context with a single Warehouse, this includes " "goods arriving to the Stock Location of this Warehouse, or " "any of its children.\n" "In a context with a single Shop, this includes goods " "arriving to the Stock Location of the Warehouse of this " "Shop, or any of its children.\n" "Otherwise, this includes goods arriving to any Stock " "Location typed as 'internal'."), 'outgoing_qty': fields.function( _product_available_thread, multi='qty_available', type='float', digits_compute=dp.get_precision('Product UoM'), string='Outgoing', help="Quantity of products that are planned to leave.\n" "In a context with a single Stock Location, this includes " "goods leaving from this Location, or any of its children.\n" "In a context with a single Warehouse, this includes " "goods leaving from the Stock Location of this Warehouse, or " "any of its children.\n" "In a context with a single Shop, this includes goods " "leaving from the Stock Location of the Warehouse of this " "Shop, or any of its children.\n" "Otherwise, this includes goods leaving from any Stock " "Location typed as 'internal'."), 'immediately_usable_qty': fields.function( _product_available_thread, digits_compute=dp.get_precision('Product UoM'), type='float', string='Immediately Usable', multi='qty_available', help="Quantity of products really available for sale." \ "Computed as: Quantity On Hand - Outgoing."), 'bom_stock': fields.function( _product_available_thread, digits_compute=dp.get_precision('Product UoM'), type='float', string='Bill of Materials Stock', help="Quantities of products based on Bill of Materials, " "useful to know how much of this " "product you could produce. " "Computed as:\n " "Reference stock of this product + " "how much could I produce of this product with the BoM" "Components", multi='qty_available'), } def unlink(self, cr, uid, ids, context=None): context = context or self.pool['res.users'].context_get(cr, uid) if self.pool['sale.order.line'].search(cr, uid, [('product_id', 'in', ids)], context=context): raise orm.except_orm( _("Error"), _("The product is used on same Sale Order, deactivate it")) if self.pool['mrp.bom'].search(cr, uid, [('product_id', 'in', ids)], context=context): raise orm.except_orm( _("Error"), _("The product is used on same BOM, deactivate it")) return super(product_product, self).unlink(cr, uid, ids, context) def copy(self, cr, uid, product_id, default=None, context=None): """Copies the product and the BoM of the product""" context = context or self.pool['res.users'].context_get(cr, uid) copy_id = super(product_product, self).copy(cr, uid, product_id, default, context) if 'bom_ids' not in default: bom_obj = self.pool['mrp.bom'] bom_ids = bom_obj.search(cr, uid, [('product_id', '=', product_id), ('bom_id', '=', False)], context=context) for bom_id in bom_ids: bom_obj.copy(cr, uid, bom_id, {'product_id': copy_id}, context=context) return copy_id def update_product_bom_price(self, cr, uid, ids, context=None): """ This Function is call by scheduler. """ context = context or self.pool['res.users'].context_get(cr, uid) for product in self.browse(cr, uid, ids, context): product.write({'standard_price': product.cost_price}) return True def update_bom_price(self, cr, uid, context=None): """ This Function is call by scheduler. """ context = context or self.pool['res.users'].context_get(cr, uid) # search product with kit product_ids = self.search(cr, uid, [('is_kit', '=', True)], context=context) for product in self.browse(cr, uid, product_ids, context): self.write(cr, uid, product.id, {'standard_price': product.cost_price}, context) return True # from profilehooks import profile # @profile(immediate=True) def update_cache_price(self, cr, uid, context=None): """ This Function is called by scheduler. """ context = context or self.pool['res.users'].context_get(cr, uid) return True if ENABLE_CACHE: if context.get('product_ids', False): product_to_browse_ids = context['product_ids'] else: cache_length = len(self.product_cost_cache) cache_ids = self.product_cost_cache.keys() cache_ids = [int(cache_id) for cache_id in cache_ids] product_ids = self.search(cr, uid, [], context=context) _logger.info(u'Cache {cache} of {product}'.format( cache=cache_length, product=len(product_ids))) product_to_browse_ids = list(set(product_ids) - set(cache_ids)) if product_to_browse_ids: _logger.setLevel(logging.WARNING) if CACHE_TYPE == 'redis': workers = multiprocessing.cpu_count() if workers > 1: workers = workers / 2 # threads = [] for split in self._chunkIt(product_to_browse_ids, workers): if split: thread = self.UpdateCachePrice( cr, uid, self, split, context) thread.start() # threads.append(thread) # for job in threads: # job.join() else: for product in self.browse(cr, uid, product_ids, context): # Get price to trigger cache calculation cost_price = product.cost_price return True def write(self, cr, uid, ids, vals, context=None): context = context or self.pool['res.users'].context_get(cr, uid) if not isinstance(ids, (list, tuple)): ids = [ids] res = super(product_product, self).write(cr, uid, ids, vals, context) changed_product = [] if ENABLE_CACHE: if 'standard_price' in vals or 'seller_ids' in vals: bom_obj = self.pool['mrp.bom'] changed_product = bom_obj.GetWhereUsed(cr, uid, ids, context)[1].keys() for product_id in changed_product: if int(product_id) in self.product_cost_cache: del self.product_cost_cache[int(product_id)] # if CACHE_TYPE == 'redis': # ctx = context.copy() # ctx['product_ids'] = [int(product_id) for product_id in changed_product] # self.update_cache_price(cr, uid, context=ctx) return res def fields_get(self, cr, uid, allfields=None, context=None): context = context or self.pool['res.users'].context_get(cr, uid) group_obj = self.pool['res.groups'] ret = super(product_product, self).fields_get(cr, uid, allfields=allfields, context=context) if not (group_obj.user_in_group( cr, uid, uid, 'product_bom.group_modify_product', context=context) or group_obj.user_in_group(cr, uid, uid, 'product_bom.group_create_product', context=context)): for fields in ret.keys(): ret[fields]['readonly'] = True return ret
class pos_order(osv.osv): _name = "pos.order" _description = "Point of Sale" _order = "id desc" def create_from_ui(self, cr, uid, orders, context=None): #_logger.info("orders: %r", orders) list = [] for order in orders: # order :: {'name': 'Order 1329148448062', 'amount_paid': 9.42, 'lines': [[0, 0, {'discount': 0, 'price_unit': 1.46, 'product_id': 124, 'qty': 5}], [0, 0, {'discount': 0, 'price_unit': 0.53, 'product_id': 62, 'qty': 4}]], 'statement_ids': [[0, 0, {'journal_id': 7, 'amount': 9.42, 'name': '2012-02-13 15:54:12', 'account_id': 12, 'statement_id': 21}]], 'amount_tax': 0, 'amount_return': 0, 'amount_total': 9.42} order_obj = self.pool.get('pos.order') # get statements out of order because they will be generated with add_payment to ensure # the module behavior is the same when using the front-end or the back-end statement_ids = order.pop('statement_ids') order_id = self.create(cr, uid, order, context) list.append(order_id) # call add_payment; refer to wizard/pos_payment for data structure # add_payment launches the 'paid' signal to advance the workflow to the 'paid' state data = { 'journal': statement_ids[0][2]['journal_id'], 'amount': order['amount_paid'], 'payment_name': order['name'], 'payment_date': statement_ids[0][2]['name'], } order_obj.add_payment(cr, uid, order_id, data, context=context) return list def unlink(self, cr, uid, ids, context=None): for rec in self.browse(cr, uid, ids, context=context): if rec.state not in ('draft', 'cancel'): raise osv.except_osv( _('Unable to Delete !'), _('In order to delete a sale, it must be new or cancelled.' )) return super(pos_order, self).unlink(cr, uid, ids, context=context) def onchange_partner_id(self, cr, uid, ids, part=False, context=None): if not part: return {'value': {}} pricelist = self.pool.get('res.partner').browse( cr, uid, part, context=context).property_product_pricelist.id return {'value': {'pricelist_id': pricelist}} def _amount_all(self, cr, uid, ids, name, args, context=None): tax_obj = self.pool.get('account.tax') cur_obj = self.pool.get('res.currency') res = {} for order in self.browse(cr, uid, ids, context=context): res[order.id] = { 'amount_paid': 0.0, 'amount_return': 0.0, 'amount_tax': 0.0, } val1 = val2 = 0.0 cur = order.pricelist_id.currency_id for payment in order.statement_ids: res[order.id]['amount_paid'] += payment.amount res[order.id]['amount_return'] += (payment.amount < 0 and payment.amount or 0) for line in order.lines: val1 += line.price_subtotal_incl val2 += line.price_subtotal res[order.id]['amount_tax'] = cur_obj.round( cr, uid, cur, val1 - val2) res[order.id]['amount_total'] = cur_obj.round(cr, uid, cur, val1) return res def _default_sale_journal(self, cr, uid, context=None): res = self.pool.get('account.journal').search(cr, uid, [('type', '=', 'sale')], limit=1) return res and res[0] or False def _default_shop(self, cr, uid, context=None): res = self.pool.get('sale.shop').search(cr, uid, []) return res and res[0] or False def copy(self, cr, uid, id, default=None, context=None): if not default: default = {} d = { 'state': 'draft', 'invoice_id': False, 'account_move': False, 'picking_id': False, 'statement_ids': [], 'nb_print': 0, 'name': self.pool.get('ir.sequence').get(cr, uid, 'pos.order'), } d.update(default) return super(pos_order, self).copy(cr, uid, id, d, context=context) _columns = { 'name': fields.char('Order Ref', size=64, required=True, states={'draft': [('readonly', False)]}, readonly=True), 'company_id': fields.many2one('res.company', 'Company', required=True, readonly=True), 'shop_id': fields.many2one('sale.shop', 'Shop', required=True, states={'draft': [('readonly', False)]}, readonly=True), 'date_order': fields.datetime('Date Ordered', readonly=True, select=True), 'user_id': fields.many2one( 'res.users', 'Connected Salesman', help= "Person who uses the the cash register. It could be a reliever, a student or an interim employee." ), 'amount_tax': fields.function(_amount_all, string='Taxes', digits_compute=dp.get_precision('Point Of Sale'), multi='all'), 'amount_total': fields.function(_amount_all, string='Total', multi='all'), 'amount_paid': fields.function(_amount_all, string='Paid', states={'draft': [('readonly', False)]}, readonly=True, digits_compute=dp.get_precision('Point Of Sale'), multi='all'), 'amount_return': fields.function(_amount_all, 'Returned', digits_compute=dp.get_precision('Point Of Sale'), multi='all'), 'lines': fields.one2many('pos.order.line', 'order_id', 'Order Lines', states={'draft': [('readonly', False)]}, readonly=True), 'statement_ids': fields.one2many('account.bank.statement.line', 'pos_statement_id', 'Payments', states={'draft': [('readonly', False)]}, readonly=True), 'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', required=True, states={'draft': [('readonly', False)]}, readonly=True), 'partner_id': fields.many2one('res.partner', 'Customer', change_default=True, select=1, states={ 'draft': [('readonly', False)], 'paid': [('readonly', False)] }), 'state': fields.selection([('draft', 'New'), ('cancel', 'Cancelled'), ('paid', 'Paid'), ('done', 'Posted'), ('invoiced', 'Invoiced')], 'State', readonly=True), 'invoice_id': fields.many2one('account.invoice', 'Invoice'), 'account_move': fields.many2one('account.move', 'Journal Entry', readonly=True), 'picking_id': fields.many2one('stock.picking', 'Picking', readonly=True), 'note': fields.text('Internal Notes'), 'nb_print': fields.integer('Number of Print', readonly=True), 'sale_journal': fields.many2one('account.journal', 'Journal', required=True, states={'draft': [('readonly', False)]}, readonly=True), } def _default_pricelist(self, cr, uid, context=None): res = self.pool.get('sale.shop').search(cr, uid, [], context=context) if res: shop = self.pool.get('sale.shop').browse(cr, uid, res[0], context=context) return shop.pricelist_id and shop.pricelist_id.id or False return False _defaults = { 'user_id': lambda self, cr, uid, context: uid, 'state': 'draft', 'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get( cr, uid, 'pos.order'), 'date_order': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), 'nb_print': 0, 'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse( cr, uid, uid, c).company_id.id, 'sale_journal': _default_sale_journal, 'shop_id': _default_shop, 'pricelist_id': _default_pricelist, } def test_paid(self, cr, uid, ids, context=None): """A Point of Sale is paid when the sum @return: True """ for order in self.browse(cr, uid, ids, context=context): if order.lines and not order.amount_total: return True if (not order.lines) or (not order.statement_ids) or \ (abs(order.amount_total-order.amount_paid) > 0.00001): return False return True def create_picking(self, cr, uid, ids, context=None): """Create a picking for each order and validate it.""" picking_obj = self.pool.get('stock.picking') partner_obj = self.pool.get('res.partner') move_obj = self.pool.get('stock.move') for order in self.browse(cr, uid, ids, context=context): if not order.state == 'draft': continue addr = order.partner_id and partner_obj.address_get( cr, uid, [order.partner_id.id], ['delivery']) or {} picking_id = picking_obj.create( cr, uid, { 'origin': order.name, 'address_id': addr.get('delivery', False), 'type': 'out', 'company_id': order.company_id.id, 'move_type': 'direct', 'note': order.note or "", 'invoice_state': 'none', 'auto_picking': True, }, context=context) self.write(cr, uid, [order.id], {'picking_id': picking_id}, context=context) location_id = order.shop_id.warehouse_id.lot_stock_id.id output_id = order.shop_id.warehouse_id.lot_output_id.id for line in order.lines: if line.product_id and line.product_id.type == 'service': continue if line.qty < 0: location_id, output_id = output_id, location_id move_obj.create(cr, uid, { 'name': line.name, 'product_uom': line.product_id.uom_id.id, 'product_uos': line.product_id.uom_id.id, 'picking_id': picking_id, 'product_id': line.product_id.id, 'product_uos_qty': abs(line.qty), 'product_qty': abs(line.qty), 'tracking_id': False, 'state': 'draft', 'location_id': location_id, 'location_dest_id': output_id, }, context=context) if line.qty < 0: location_id, output_id = output_id, location_id wf_service = netsvc.LocalService("workflow") wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr) picking_obj.force_assign(cr, uid, [picking_id], context) return True def set_to_draft(self, cr, uid, ids, *args): if not len(ids): return False for order in self.browse(cr, uid, ids, context=context): if order.state <> 'cancel': raise osv.except_osv( _('Error!'), _('In order to set to draft a sale, it must be cancelled.') ) self.write(cr, uid, ids, {'state': 'draft'}) wf_service = netsvc.LocalService("workflow") for i in ids: wf_service.trg_create(uid, 'pos.order', i, cr) return True def cancel_order(self, cr, uid, ids, context=None): """ Changes order state to cancel @return: True """ stock_picking_obj = self.pool.get('stock.picking') for order in self.browse(cr, uid, ids, context=context): wf_service.trg_validate(uid, 'stock.picking', order.picking_id.id, 'button_cancel', cr) if stock_picking_obj.browse( cr, uid, order.picking_id.id, context=context).state <> 'cancel': raise osv.except_osv(_('Error!'), _('Unable to cancel the picking.')) self.write(cr, uid, ids, {'state': 'cancel'}, context=context) return True def add_payment(self, cr, uid, order_id, data, context=None): """Create a new payment for the order""" statement_obj = self.pool.get('account.bank.statement') statement_line_obj = self.pool.get('account.bank.statement.line') prod_obj = self.pool.get('product.product') property_obj = self.pool.get('ir.property') curr_c = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id curr_company = curr_c.id order = self.browse(cr, uid, order_id, context=context) ids_new = [] args = { 'amount': data['amount'], } if 'payment_date' in data.keys(): args['date'] = data['payment_date'] args['name'] = order.name if data.get('payment_name', False): args['name'] = args['name'] + ': ' + data['payment_name'] account_def = property_obj.get(cr, uid, 'property_account_receivable', 'res.partner', context=context) args['account_id'] = (order.partner_id and order.partner_id.property_account_receivable \ and order.partner_id.property_account_receivable.id) or (account_def and account_def.id) or False args['partner_id'] = order.partner_id and order.partner_id.id or None if not args['account_id']: if not args['partner_id']: msg = _( 'There is no receivable account defined to make payment') else: msg = _( 'There is no receivable account defined to make payment for the partner: "%s" (id:%d)' ) % ( order.partner_id.name, order.partner_id.id, ) raise osv.except_osv(_('Configuration Error !'), msg) statement_id = statement_obj.search( cr, uid, [('journal_id', '=', int(data['journal'])), ('company_id', '=', curr_company), ('user_id', '=', uid), ('state', '=', 'open')], context=context) if len(statement_id) == 0: raise osv.except_osv(_('Error !'), _('You have to open at least one cashbox')) if statement_id: statement_id = statement_id[0] args['statement_id'] = statement_id args['pos_statement_id'] = order_id args['journal_id'] = int(data['journal']) args['type'] = 'customer' args['ref'] = order.name statement_line_obj.create(cr, uid, args, context=context) ids_new.append(statement_id) wf_service = netsvc.LocalService("workflow") wf_service.trg_validate(uid, 'pos.order', order_id, 'paid', cr) wf_service.trg_write(uid, 'pos.order', order_id, cr) return statement_id def refund(self, cr, uid, ids, context=None): """Create a copy of order for refund order""" clone_list = [] line_obj = self.pool.get('pos.order.line') for order in self.browse(cr, uid, ids, context=context): clone_id = self.copy(cr, uid, order.id, { 'name': order.name + ' REFUND', }, context=context) clone_list.append(clone_id) for clone in self.browse(cr, uid, clone_list, context=context): for order_line in clone.lines: line_obj.write(cr, uid, [order_line.id], {'qty': -order_line.qty}, context=context) new_order = ','.join(map(str, clone_list)) abs = { #'domain': "[('id', 'in', ["+new_order+"])]", 'name': _('Return Products'), 'view_type': 'form', 'view_mode': 'form', 'res_model': 'pos.order', 'res_id': clone_list[0], 'view_id': False, 'context': context, 'type': 'ir.actions.act_window', 'nodestroy': True, 'target': 'current', } return abs def action_invoice_state(self, cr, uid, ids, context=None): return self.write(cr, uid, ids, {'state': 'invoiced'}, context=context) def action_invoice(self, cr, uid, ids, context=None): wf_service = netsvc.LocalService("workflow") inv_ref = self.pool.get('account.invoice') inv_line_ref = self.pool.get('account.invoice.line') product_obj = self.pool.get('product.product') inv_ids = [] for order in self.pool.get('pos.order').browse(cr, uid, ids, context=context): if order.invoice_id: inv_ids.append(order.invoice_id.id) continue if not order.partner_id: raise osv.except_osv( _('Error'), _('Please provide a partner for the sale.')) acc = order.partner_id.property_account_receivable.id inv = { 'name': order.name, 'origin': order.name, 'account_id': acc, 'journal_id': order.sale_journal.id or None, 'type': 'out_invoice', 'reference': order.name, 'partner_id': order.partner_id.id, 'comment': order.note or '', 'currency_id': order.pricelist_id.currency_id. id, # considering partner's sale pricelist's currency } inv.update( inv_ref.onchange_partner_id(cr, uid, [], 'out_invoice', order.partner_id.id)['value']) if not inv.get('account_id', None): inv['account_id'] = acc inv_id = inv_ref.create(cr, uid, inv, context=context) self.write(cr, uid, [order.id], { 'invoice_id': inv_id, 'state': 'invoiced' }, context=context) inv_ids.append(inv_id) for line in order.lines: inv_line = { 'invoice_id': inv_id, 'product_id': line.product_id.id, 'quantity': line.qty, } inv_name = product_obj.name_get(cr, uid, [line.product_id.id], context=context)[0][1] inv_line.update( inv_line_ref.product_id_change( cr, uid, [], line.product_id.id, line.product_id.uom_id.id, line.qty, partner_id=order.partner_id.id, fposition_id=order.partner_id. property_account_position.id)['value']) if line.product_id.description_sale: inv_line['note'] = line.product_id.description_sale inv_line['price_unit'] = line.price_unit inv_line['discount'] = line.discount inv_line['name'] = inv_name inv_line['invoice_line_tax_id'] = ('invoice_line_tax_id' in inv_line)\ and [(6, 0, inv_line['invoice_line_tax_id'])] or [] inv_line_ref.create(cr, uid, inv_line, context=context) inv_ref.button_reset_taxes(cr, uid, [inv_id], context=context) wf_service.trg_validate(uid, 'pos.order', order.id, 'invoice', cr) if not inv_ids: return {} mod_obj = self.pool.get('ir.model.data') res = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_form') res_id = res and res[1] or False return { 'name': _('Customer Invoice'), 'view_type': 'form', 'view_mode': 'form', 'view_id': [res_id], 'res_model': 'account.invoice', 'context': "{'type':'out_invoice'}", 'type': 'ir.actions.act_window', 'nodestroy': True, 'target': 'current', 'res_id': inv_ids and inv_ids[0] or False, } def create_account_move(self, cr, uid, ids, context=None): """Create a account move line of order grouped by products or not.""" account_move_obj = self.pool.get('account.move') account_move_line_obj = self.pool.get('account.move.line') account_period_obj = self.pool.get('account.period') period = account_period_obj.find(cr, uid, context=context)[0] account_tax_obj = self.pool.get('account.tax') res_obj = self.pool.get('res.users') property_obj = self.pool.get('ir.property') for order in self.browse(cr, uid, ids, context=context): if order.state <> 'paid': continue curr_c = res_obj.browse(cr, uid, uid).company_id comp_id = res_obj.browse(cr, order.user_id.id, order.user_id.id).company_id comp_id = comp_id and comp_id.id or False to_reconcile = [] group_tax = {} account_def = property_obj.get(cr, uid, 'property_account_receivable', 'res.partner', context=context).id order_account = order.partner_id and order.partner_id.property_account_receivable and order.partner_id.property_account_receivable.id or account_def or curr_c.account_receivable.id # Create an entry for the sale move_id = account_move_obj.create( cr, uid, { 'journal_id': order.sale_journal.id, }, context=context) # Create an move for each order line for line in order.lines: tax_amount = 0 taxes = [t for t in line.product_id.taxes_id] computed = account_tax_obj.compute_all( cr, uid, taxes, line.price_unit * (100.0 - line.discount) / 100.0, line.qty) computed_taxes = computed['taxes'] for tax in computed_taxes: tax_amount += round(tax['amount'], 2) group_key = (tax['tax_code_id'], tax['base_code_id'], tax['account_collected_id']) if group_key in group_tax: group_tax[group_key] += round(tax['amount'], 2) else: group_tax[group_key] = round(tax['amount'], 2) amount = line.price_subtotal # Search for the income account if line.product_id.property_account_income.id: income_account = line.product_id.property_account_income.id elif line.product_id.categ_id.property_account_income_categ.id: income_account = line.product_id.categ_id.property_account_income_categ.id else: raise osv.except_osv(_('Error !'), _('There is no income '\ 'account defined for this product: "%s" (id:%d)') \ % (line.product_id.name, line.product_id.id, )) # Empty the tax list as long as there is no tax code: tax_code_id = False tax_amount = 0 while computed_taxes: tax = computed_taxes.pop(0) if amount > 0: tax_code_id = tax['base_code_id'] tax_amount = line.price_subtotal * tax['base_sign'] else: tax_code_id = tax['ref_base_code_id'] tax_amount = line.price_subtotal * tax['ref_base_sign'] # If there is one we stop if tax_code_id: break # Create a move for the line account_move_line_obj.create( cr, uid, { 'name': line.name, 'date': order.date_order[:10], 'ref': order.name, 'quantity': line.qty, 'product_id': line.product_id.id, 'move_id': move_id, 'account_id': income_account, 'company_id': comp_id, 'credit': ((amount > 0) and amount) or 0.0, 'debit': ((amount < 0) and -amount) or 0.0, 'journal_id': order.sale_journal.id, 'period_id': period, 'tax_code_id': tax_code_id, 'tax_amount': tax_amount, 'partner_id': order.partner_id and order.partner_id.id or False }, context=context) # For each remaining tax with a code, whe create a move line for tax in computed_taxes: if amount > 0: tax_code_id = tax['base_code_id'] tax_amount = line.price_subtotal * tax['base_sign'] else: tax_code_id = tax['ref_base_code_id'] tax_amount = line.price_subtotal * tax['ref_base_sign'] if not tax_code_id: continue account_move_line_obj.create( cr, uid, { 'name': "Tax" + line.name, 'date': order.date_order[:10], 'ref': order.name, 'product_id': line.product_id.id, 'quantity': line.qty, 'move_id': move_id, 'account_id': income_account, 'company_id': comp_id, 'credit': 0.0, 'debit': 0.0, 'journal_id': order.sale_journal.id, 'period_id': period, 'tax_code_id': tax_code_id, 'tax_amount': tax_amount, }, context=context) # Create a move for each tax group (tax_code_pos, base_code_pos, account_pos) = (0, 1, 2) for key, amount in group_tax.items(): account_move_line_obj.create( cr, uid, { 'name': 'Tax', 'date': order.date_order[:10], 'ref': order.name, 'move_id': move_id, 'company_id': comp_id, 'quantity': line.qty, 'product_id': line.product_id.id, 'account_id': key[account_pos], 'credit': ((amount > 0) and amount) or 0.0, 'debit': ((amount < 0) and -amount) or 0.0, 'journal_id': order.sale_journal.id, 'period_id': period, 'tax_code_id': key[tax_code_pos], 'tax_amount': amount, }, context=context) # counterpart to_reconcile.append(account_move_line_obj.create(cr, uid, { 'name': order.name, 'date': order.date_order[:10], 'ref': order.name, 'move_id': move_id, 'company_id': comp_id, 'account_id': order_account, 'credit': ((order.amount_total < 0) and -order.amount_total)\ or 0.0, 'debit': ((order.amount_total > 0) and order.amount_total)\ or 0.0, 'journal_id': order.sale_journal.id, 'period_id': period, 'partner_id': order.partner_id and order.partner_id.id or False }, context=context)) self.write(cr, uid, order.id, { 'state': 'done', 'account_move': move_id }, context=context) return True def action_payment(self, cr, uid, ids, context=None): return self.write(cr, uid, ids, {'state': 'payment'}, context=context) def action_paid(self, cr, uid, ids, context=None): context = context or {} self.create_picking(cr, uid, ids, context=None) self.write(cr, uid, ids, {'state': 'paid'}, context=context) return True def action_cancel(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'state': 'cancel'}, context=context) return True def action_done(self, cr, uid, ids, context=None): self.create_account_move(cr, uid, ids, context=context) return True
class wiz_crear_factura(osv.osv_memory): _name = 'wiz.crear.factura' _description = 'Asistente para crear las facturas' _columns = { 'partner_id': fields.many2one('res.partner', 'Empresa', readonly=True), 'journal_id': fields.many2one('account.journal', 'Diario', domain=[('type', '=', 'purchase')], required=True), 'description': fields.char('Descripción', size=64, required=True), 'importe': fields.float('Importe', digits_compute=dp.get_precision('Account')), 'pago': fields.integer('Pago'), 'type': fields.char('Tipo de Pago', size=1), } def default_get(self, cr, uid, fields_list, context=None): values = {} if context['active_model'] == "l10n.es.tesoreria.pagos.var.plan": obj = self.pool.get('l10n.es.tesoreria.pagos.var.plan') type = 'V' else: obj = self.pool.get('l10n.es.tesoreria.pagos.period.plan') type = 'P' for pago in obj.browse(cr, uid, context['active_ids']): if pago.factura_id: raise osv.except_osv( _('Error!'), _('Este pago ya tiene una factura asignado!!')) values = { 'partner_id': pago.partner_id.id, 'journal_id': pago.diario.id, 'description': pago.name, 'importe': pago.importe, 'pago': int(pago.id), 'type': type, } return values def button_create_inv(self, cr, uid, ids, context=None): invoice_obj = self.pool.get('account.invoice') invoice_line_obj = self.pool.get('account.invoice.line') address_obj = self.pool.get('res.partner.address') for wiz in self.browse(cr, uid, ids): address = address_obj.search( cr, uid, [('partner_id', '=', wiz.partner_id.id)]) if address: values = { 'name': 'Prev: ' + wiz.description + '/ Importe: ' + str(wiz.importe), 'reference': 'Prev: ' + wiz.description + '/ Importe: ' + str(wiz.importe), 'partner_id': wiz.partner_id.id, 'journal_id': wiz.journal_id.id, 'address_invoice_id': address[0], 'type': 'in_invoice', 'account_id': wiz.partner_id.property_account_receivable.id, } if wiz.partner_id.property_payment_term: values.update({ 'payment_term': wiz.partner_id.property_payment_term.id }) if wiz.partner_id.payment_type_customer: values.update({ 'payment_type': wiz.partner_id.payment_type_customer.id }) if wiz.partner_id.property_account_position: values.update({ 'fiscal_position': wiz.partner_id.property_account_position.id }) else: raise osv.except_osv(_('Error!'), _('Address not found for Partner: '), wiz.partner_id.name) invoice_id = invoice_obj.create(cr, uid, values) if wiz.type == 'V': obj = self.pool.get('l10n.es.tesoreria.pagos.var.plan') else: obj = self.pool.get('l10n.es.tesoreria.pagos.period.plan') obj.write(cr, uid, wiz.pago, { 'factura_id': invoice_id, 'diario': wiz.journal_id.id, 'pagado': 1 }) return {'type': 'ir.actions.act_window_close'}
('confirmed4', 'Waiting CEO Approve'), ('approved', 'Approved'), ('except_picking', 'Shipping Exception'), ('except_invoice', 'Invoice Exception'), ('done', 'Done'), ('cancel', 'Cancelled') ] _columns = { 'state' : fields.selection(STATE_SELECTION, 'State', readonly=True, help="The state of the purchase order or the quotation request. A quotation is a purchase order in a 'Draft' state. Then the order has to be confirmed by the user, the state switch to 'Confirmed'. Then the supplier must confirm the order to change the state to 'Approved'. When the purchase order is paid and received, the state becomes 'Done'. If a cancel action occurs in the invoice or in the reception of goods, the state becomes in exception.", select=True), 'budget_info_ids_po' : fields.many2many('budget.info.po', 'budget_info_rel_po', 'order_id', 'budget_info_id_po', 'Budget Line', readonly=True), 'budget_note' : fields.text('Budget Note'), 'budget_note_line_ids' : fields.one2many('budget.note.po', 'order_id', 'Budget Note History'), #######DICOUNT##################### 'amount_untaxed': fields.function(_amount_all, method=True, digits_compute= dp.get_precision('Purchase Price'), string='Untaxed Amount', store={ 'purchase.order.line': (_get_order, None, 10), 'purchase.order': (lambda self, cr, uid, ids, c={}: ids, ['discount_total'], 20), }, multi="sums", help="The amount without tax"), 'amount_tax': fields.function(_amount_all, method=True, digits_compute= dp.get_precision('Purchase Price'), string='Taxes', store={ 'purchase.order.line': (_get_order, None, 10), 'purchase.order': (lambda self, cr, uid, ids, c={}: ids, ['discount_total'], 20), }, multi="sums", help="The tax amount"), 'amount_total': fields.function(_amount_all, method=True, digits_compute= dp.get_precision('Purchase Price'), string='Total', store={ 'purchase.order.line': (_get_order, None, 10), 'purchase.order': (lambda self, cr, uid, ids, c={}: ids, ['discount_total'], 20), }, multi="sums",help="The total amount"), 'discount_total': fields.float('Discount Total(%)', digits=(16,2)),
class changes_production_lot_stock(osv.osv): def default_get(self, cr, uid, ids, fields, context=None): if context is None: context = {} res = {} company_id = self.pool.get('res.company')._company_default_get( cr, uid, 'changes.production.lot.stock', context=context), print "company_id", company_id res.update({'company_id': company_id[0]}) return res _name = 'changes.production.lot.stock' _columns = { 'name': fields.many2one('stock.production.lot', 'Lot number'), 'stock_driver': fields.boolean('Driver'), 'product_id': fields.many2one('product.product', 'Product'), 'company_id': fields.many2one('res.company', 'Company', required=True), 'factor': fields.integer('Factor', help="This field define the field in readonly"), 'location_id': fields.char('Location', 128), 'length': fields.float('Length (m)', digits_compute=dp.get_precision('Extra UOM data')), 'heigth': fields.float('Heigth (m)', digits_compute=dp.get_precision('Extra UOM data')), 'diff': fields.float('Diff', digits_compute=dp.get_precision('Product UoM')), 'width': fields.float('Width (m)', digits_compute=dp.get_precision('Extra UOM data')), 'quantity': fields.float('Quantity', digits_compute=dp.get_precision('Product UoM')), 'pieces_qty': fields.integer('Pieces'), 'new_location_id': fields.char('New Location', 128), 'new_length': fields.float('New Length (m)', digits_compute=dp.get_precision('Extra UOM data')), 'new_heigth': fields.float('New Heigth (m)', digits_compute=dp.get_precision('Extra UOM data')), 'new_width': fields.float(' New Width (m)', digits_compute=dp.get_precision('Extra UOM data')), 'new_quantity': fields.float('New Quantity', digits_compute=dp.get_precision('Product UoM')), 'new_pieces_qty': fields.integer('New Pieces'), 'description_stock_id': fields.many2one('descriptions.changes.production.lot.stock', 'Time Descriptions'), } _defaults = { 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get( cr, uid, 'changes.production.lot.stock', context=c), } def create(self, cr, uid, vals, context=None): """ Create a new register with thes fields reandonly that dot save in the data base """ company_id = self.pool.get('res.company')._company_default_get( cr, uid, 'purchase.order.line', context=context), print "valssss", vals if vals.get('name', False): lot_obj = self.pool.get('stock.production.lot') product_obj = self.pool.get('product.uom') lot_brw = lot_obj.browse(cr, uid, vals['name'], context=None) pieces = product_obj._compute_pieces2( cr, uid, lot_brw.product_id.stock_driver, lot_brw.virtual, lot_brw.length, lot_brw.heigth, lot_brw.width) area_old = lot_brw.virtual if lot_brw.product_id.stock_driver == 'tile': area_new = product_obj._compute_area( cr, uid, lot_brw.product_id.stock_driver, vals['new_pieces_qty'], lot_brw.product_id.tile_format_id.length, lot_brw.product_id.tile_format_id.heigth, lot_brw.width) diff = area_new - area_old vals.update({ 'diff': diff, 'new_quantity': area_new, 'length': lot_brw.product_id.tile_format_id.length, 'new_length': lot_brw.product_id.tile_format_id.length, 'heigth': lot_brw.product_id.tile_format_id.heigth, 'new_heigth': lot_brw.product_id.tile_format_id.heigth, 'width': lot_brw.width, 'pieces_qty': pieces, 'quantity': lot_brw.virtual, 'company_id': company_id[0] }) if lot_brw.product_id.stock_driver == 'slab': area_new = product_obj._compute_area( cr, uid, lot_brw.product_id.stock_driver, vals['new_pieces_qty'], vals['new_length'], vals['new_heigth'], lot_brw.width) diff = area_new - area_old vals.update({ 'diff': diff, 'new_quantity': area_new, 'length': vals['new_length'], 'heigth': vals['new_heigth'], 'width': lot_brw.width, 'pieces_qty': pieces, 'quantity': lot_brw.virtual, 'company_id': company_id[0] }) if lot_brw.product_id.stock_driver == 'block': area_new = product_obj._compute_area( cr, uid, lot_brw.product_id.stock_driver, vals['new_pieces_qty'], vals['new_length'], vals['new_heigth'], lot_brw.width) diff = area_new - area_old vals.update({ 'diff': diff, 'new_quantity': area_new, 'length': vals['new_length'], 'heigth': vals['new_heigth'], 'width': vals['new_width'], 'pieces_qty': pieces, 'quantity': lot_brw.virtual, 'company_id': company_id[0] }) return super(changes_production_lot_stock, self).create(cr, uid, vals, context) def write(self, cr, uid, ids, vals, context=None): """ Chages for save changes in the field readonly """ if context is None: context = {} if 'name' in vals: lot_obj = self.pool.get('stock.production.lot') product_obj = self.pool.get('product.uom') lot_brw = lot_obj.browse(cr, uid, vals['name'], context=None) pieces = product_obj._compute_pieces2( cr, uid, lot_brw.product_id.stock_driver, lot_brw.virtual, lot_brw.length, lot_brw.heigth, lot_brw.width) area_old = lot_brw.virtual if lot_brw.product_id.stock_driver == 'tile': area_new = product_obj._compute_area( cr, uid, lot_brw.product_id.stock_driver, vals['new_pieces_qty'], lot_brw.product_id.tile_format_id.length, lot_brw.product_id.tile_format_id.heigth, lot_brw.width) diff = area_new - area_old vals.update({ 'diff': diff, 'new_quantity': area_new, 'length': lot_brw.product_id.tile_format_id.length, 'new_length': lot_brw.product_id.tile_format_id.length, 'heigth': lot_brw.product_id.tile_format_id.heigth, 'new_heigth': lot_brw.product_id.tile_format_id.heigth, 'width': lot_brw.width, 'pieces_qty': pieces, 'quantity': lot_brw.virtual, 'company_id': company_id[0] }) if lot_brw.product_id.stock_driver == 'slab': area_new = product_obj._compute_area( cr, uid, lot_brw.product_id.stock_driver, vals['new_pieces_qty'], vals['new_length'], vals['new_heigth'], lot_brw.width) diff = area_new - area_old vals.update({ 'diff': diff, 'new_quantity': area_new, 'length': vals['new_length'], 'heigth': vals['new_heigth'], 'width': lot_brw.width, 'pieces_qty': pieces, 'quantity': lot_brw.virtual, 'company_id': company_id[0] }) if lot_brw.product_id.stock_driver == 'block': area_new = product_obj._compute_area( cr, uid, lot_brw.product_id.stock_driver, vals['new_pieces_qty'], vals['new_length'], vals['new_heigth'], lot_brw.width) diff = area_new - area_old vals.update({ 'diff': diff, 'new_quantity': area_new, 'length': vals['new_length'], 'heigth': vals['new_heigth'], 'width': vals['new_width'], 'pieces_qty': pieces, 'quantity': lot_brw.virtual, 'company_id': company_id[0] }) return super(changes_production_lot_stock, self).write(cr, uid, ids, vals, context=context) def unlink(self, cr, uid, ids, context=None): """ Modified to avoid delete a line of change confirmed """ changes_orders = self.browse(cr, uid, ids, context=context) unlink_ids = [] for s in changes_orders: if s.description_stock_id.state in ('draft'): unlink_ids.append(s.id) else: raise osv.except_osv( _('Invalid action !'), _('Cannot delete this changes which are already confirmed !' )) return super(changes_production_lot_stock, self).unink(cr, uid, unlink_ids, context=context) def onchange_changes_stock(self, cr, uid, ids, name, length, heigth, width, quantity, new_length, new_heigth, new_width, new_pieces_qty, context=None): print "ids", ids if context is None: context = {} res = {'value': {}} lot_obj = self.pool.get('stock.production.lot') stock_move_obj = self.pool.get('stock.move') product_obj = self.pool.get('product.uom') if name: lot_brw = lot_obj.browse(cr, uid, name, context=None) area_org = quantity if lot_brw.product_id.stock_driver in ('slab', 'block'): new_pieces_qty = 1 res['value'].update({'new_pieces_qty': new_pieces_qty}) area_new = product_obj._compute_area( cr, uid, lot_brw.product_id.stock_driver, new_pieces_qty, new_length, new_heigth, new_width) diff = area_new - area_org res['value'].update({'new_quantity': area_new}) res['value'].update({'diff': diff}) return res def onchage_default(self, cr, uid, ids, lot_number, context=None): """ @param lot_number id of the lot for add the values by fefault """ if context is None: context = {} res = {'value': {}} lot_obj = self.pool.get('stock.production.lot') stock_move_obj = self.pool.get('stock.move') product_obj = self.pool.get('product.uom') if lot_number: lot_brw = lot_obj.browse(cr, uid, lot_number, context=None) res['value'].update({'length': lot_brw.length}) res['value'].update({'heigth': lot_brw.heigth}) res['value'].update({'width': lot_brw.width}) res['value'].update({'quantity': lot_brw.virtual}) res['value'].update({ 'pieces_qty': product_obj._compute_pieces2(cr, uid, lot_brw.product_id.stock_driver, lot_brw.virtual, lot_brw.length, lot_brw.heigth, lot_brw.width) }) if lot_brw.product_id.stock_driver == 'normal': res['value'].update({'factor': 3}) res['value'].update({'new_length': lot_brw.length}) res['value'].update({'new_heigth': lot_brw.heigth}) res['value'].update({'new_width': lot_brw.width}) if lot_brw.product_id.stock_driver == 'tile': res['value'].update({'factor': 2}) res['value'].update( {'new_length': lot_brw.product_id.tile_format_id.length}) res['value'].update( {'new_heigth': lot_brw.product_id.tile_format_id.heigth}) if lot_brw.product_id.stock_driver == 'slab': res['value'].update({'factor': 1}) res['value'].update({'new_length': lot_brw.length}) res['value'].update({'new_heigth': lot_brw.heigth}) res['value'].update({'new_width': lot_brw.width}) if lot_brw.product_id.stock_driver == 'block': res['value'].update({'factor': 0}) res['value'].update({'new_length': lot_brw.length}) res['value'].update({'new_heigth': lot_brw.heigth}) res['value'].update({'new_width': lot_brw.width}) return res
def agg_righe_provdoc(self, cr, uid, doc_id, context): if doc_id: if type(doc_id)==type([]): doc = self.pool.get('fiscaldoc.header').browse(cr,uid,doc_id)[0] else: doc = self.pool.get('fiscaldoc.header').browse(cr,uid,doc_id) if doc.tipo_doc.tipo_documento <> 'DT': part = doc.partner_id lista={} lista2={} for riga in doc.righe_articoli: if riga.name.sconto_partner or riga.name.sconto_pagamento: netto = riga.totale_riga if riga.name.sconto_partner: netto = netto-(netto*riga.name.sconto_partner/100) netto = arrot(cr,uid,netto,dp.get_precision('Account')) if riga.name.sconto_pagamento: netto = netto-(netto*riga.name.sconto_pagamento/100) netto = arrot(cr,uid,netto,dp.get_precision('Account')) else: netto = riga.totale_riga netto = arrot(cr,uid,netto,dp.get_precision('Account')) if lista.get(riga.perc_provv,False): # esiste già una riga di provvigione uguale dati_record = lista[riga.perc_provv] imponibile = netto provvigione = imponibile * riga.perc_provv/100 dati_record['imponibile'] += imponibile if doc.tipo_doc.tipo_documento <> 'NC': dati_record['provvigione'] += provvigione else: dati_record['provvigione'] += provvigione*-1 lista[riga.perc_provv] = dati_record else: # nuova riga di provvigione imponibile = netto if doc.tipo_doc.tipo_documento <> 'NC': provvigione = imponibile * riga.perc_provv/100 else: provvigione = imponibile * riga.perc_provv/100*-1 dati_record = {'imponibile':imponibile,'provvigione':provvigione} lista.update({riga.perc_provv:dati_record}) if lista2.get(riga.perc_provvag2,False): # esiste già una riga di provvigione uguale dati_record = lista2[riga.perc_provvag2] imponibile = netto provvigione = imponibile * riga.perc_provvag2/100 dati_record['imponibile'] += imponibile if doc.tipo_doc.tipo_documento <> 'NC': dati_record['provvigione'] += provvigione else: dati_record['provvigione'] += provvigione*-1 lista2[riga.perc_provvag2] = dati_record else: # nuova riga di provvigione imponibile = netto if doc.tipo_doc.tipo_documento <> 'NC': provvigione = imponibile * riga.perc_provvag2/100 else: provvigione = imponibile * riga.perc_provvag2/100*-1 dati_record = {'imponibile':imponibile,'provvigione':provvigione} lista.update({riga.perc_provvag2:dati_record}) lines = self.search(cr, uid, [('name', '=', part.agent_id.id),('documento_id','=',doc.id)]) if lines: res = self.unlink(cr, uid, lines, context) if lista: for riga in lista: if lista[riga]['provvigione']<>0: record = { 'name':part.agent_id.id, 'documento_id':doc.id, 'imponibile':lista[riga]['imponibile'], 'provvigione':lista[riga]['provvigione'], 'commission_rate':riga, } id_r= self.create(cr,uid,record) lines = self.search(cr, uid, [('name', '=', part.agent2_id.id),('documento_id','=',doc.id)]) if lines: res = self.unlink(cr, uid, lines, context) if lista2: for riga in lista2: record = { 'name':part.agent2_id.id, 'documento_id':doc.id, 'imponibile':lista2[riga]['imponibile'], 'provvigione':lista2[riga]['provvigione'], 'commission_rate':riga, } id_r= self.create(cr,uid,record) return True
class product_variant_dimension_value(osv.osv): _name = "product.variant.dimension.value" _description = "Dimension Value" _rec_name = "option_id" def unlink(self, cr, uid, ids, context=None): for value in self.browse(cr, uid, ids, context=context): if value.product_ids: product_list = '\n - ' + '\n - '.join( [product.name for product in value.product_ids]) raise osv.except_osv( _('Dimension value can not be removed'), _("The value %s is used in the product : %s \n Please remove this product before removing the value" % (value.option_id.name, product_list))) return super(product_variant_dimension_value, self).unlink(cr, uid, ids, context) def _get_dimension_values(self, cr, uid, ids, context=None): return self.pool.get('product.variant.dimension.value').search( cr, uid, [('dimension_id', 'in', ids)], context=context) _columns = { 'option_id': fields.many2one('product.variant.dimension.option', 'Option', required=True), 'sequence': fields.integer('Sequence'), 'price_extra': fields.float('Sale Price Extra', digits_compute=dp.get_precision('Sale Price')), 'price_margin': fields.float('Sale Price Margin', digits_compute=dp.get_precision('Sale Price')), 'cost_price_extra': fields.float('Cost Price Extra', digits_compute=dp.get_precision('Purchase Price')), 'dimension_id': fields.related('option_id', 'dimension_id', type="many2one", relation="product.variant.dimension.type", string="Dimension Type", store=True), 'product_tmpl_id': fields.many2one('product.template', 'Product Template', ondelete='cascade'), 'dimension_sequence': fields.related( 'dimension_id', 'sequence', string= "Related Dimension Sequence", #used for ordering purposes in the "variants" store={ 'product.variant.dimension.type': (_get_dimension_values, ['sequence'], 10), }), 'product_ids': fields.many2many('product.product', 'product_product_dimension_rel', 'dimension_id', 'product_id', 'Variant', readonly=True), 'active': fields.boolean( 'Active?', help= "If false, this value will be not use anymore for generating variant" ), } _defaults = { 'active': lambda *a: 1, } _order = "dimension_sequence, sequence, option_id"
class stock_picking_out(osv.osv): _inherit = "stock.picking.out" def _total_weight_net(self, cr, uid, ids, field_name, arg, context=None): return self.pool.get('stock.picking')._total_weight_net( cr, uid, ids, field_name, arg, context) def _get_order(self, cr, uid, ids, context=None): return self.pool.get('stock.picking')._get_order(cr, uid, ids, context) def _total_ord_weight_net(self, cr, uid, ids, field_name, arg, context=None): return self.pool.get('stock.picking')._total_ord_weight_net( cr, uid, ids, field_name, arg, context) def _get_company_code(self, cr, user, context=None): return self.pool.get('stock.picking')._get_company_code( cr, user, context) def onchange_logis_company(self, cr, uid, ids, logistic_company_id, context=None): return self.pool.get('stock.picking').onchange_logis_company( cr, uid, ids, logistic_company_id, context) def distribute_weight(self, cr, uid, ids, context=None): return self.pool.get('stock.picking').distribute_weight( cr, uid, ids, context) def process_ship(self, cr, uid, ids, context=None): return self.pool.get('stock.picking').process_ship( cr, uid, ids, context) _columns = { 'logis_company': fields.many2one( 'logistic.company', 'Shipper Company', help='Name of the Logistics company providing the shipper services.' ), 'freight': fields.boolean( 'Shipment', help='Indicates if the shipment is a freight shipment.'), 'sat_delivery': fields.boolean( 'Saturday Delivery', help='Indicates is it is appropriate to send delivery on Saturday.' ), 'package_type': fields.selection([('01', 'Letter'), ('02', 'Customer Supplied Package'), ('03', 'Tube'), ('04', 'PAK'), ('21', 'ExpressBox'), ('24', '25KG Box'), ('25', '10KG Box'), ('30', 'Pallet'), ('2a', 'Small Express Box'), ('2b', 'Medium Express Box'), ('2c', 'Large Express Box')], 'Package Type', help='Indicates the type of package'), 'bill_shipping': fields.selection([('shipper', 'Shipper'), ('receiver', 'Receiver'), ('thirdparty', 'Third Party')], 'Bill Shipping to', help='Shipper, Receiver, or Third Party.'), 'with_ret_service': fields.boolean( 'With Return Services', help='Include Return Shipping Information in the package.'), 'tot_ship_weight': fields.function( _total_weight_net, method=True, type='float', digits=(16, 3), string='Shipment Weight', store={ 'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['packages_ids'], -10), 'stock.packages': (_get_order, ['weight'], -10), }, help= "Adds the Total Weight of all the packages in the Packages Table.", ), 'tot_del_order_weight': fields.function( _total_ord_weight_net, method=True, readonly=True, string='Total Order Weight', store=False, help= "Adds the Total Weight of all the packages in the Packages Table." ), 'packages_ids': fields.one2many("stock.packages", 'pick_id', 'Packages Table'), 'shipcharge': fields.float('Shipping Cost', readonly=True), 'ship_state': fields.selection([('draft', 'Draft'), ('in_process', 'In Process'), ('ready_pick', 'Ready for Pickup'), ('shipped', 'Shipped'), ('delivered', 'Delivered'), ('void', 'Void'), ('hold', 'Hold'), ('cancelled', 'Cancelled')], 'Shipping Status', readonly=True, help='The current status of the shipment'), 'trade_mark': fields.text('Trademarks AREA'), 'ship_message': fields.text('Message'), 'address_validate': fields.selection([('validate', 'Validate'), ('nonvalidate', 'No Validation')], 'Address Validation', help=''' No Validation = No address validation. Validate = Fail on failed address validation. Defaults to validate. Note: Full address validation is not performed. Therefore, it is the responsibility of the Shipping Tool User to ensure the address entered is correct to avoid an address correction fee.''' ), 'ship_description': fields.text('Description'), 'ship_from': fields.boolean( 'Ship From', help= 'Required if pickup location is different from the shipper\'s address..' ), 'ship_from_tax_id_no': fields.char('Identification Number', size=64, select=1), 'ship_from_address': fields.many2one('res.partner', 'Ship From Address'), # 'address': fields.many2one('res.partner', 'Ship From Address'), 'tot_order_weight': fields.related('sale_id', 'total_weight_net', type='float', relation='sale.order', string='Total Order Weight'), 'comm_inv': fields.boolean('Commercial Invoice'), 'cer_orig': fields.boolean('U.S. Certificate of Origin'), 'nafta_cer_orig': fields.boolean('NAFTA Certificate of Origin'), 'sed': fields.boolean('Shipper Export Declaration (SED)'), 'prod_option': fields.selection([('01', 'AVAILABLE TO CUSTOMS UPON REQUEST'), ('02', 'SAME AS EXPORTER'), ('03', 'ATTACHED LIST'), ('04', 'UNKNOWN'), (' ', ' ')], 'Option'), 'prod_company': fields.char( 'CompanyName', size=256, help='Only applicable when producer option is empty or not present.' ), 'prod_tax_id_no': fields.char( 'TaxIdentificationNumber', size=256, help='Only applicable when producer option is empty or not present.' ), 'prod_address_id': fields.many2one( 'res.partner', 'Producer Address', help='Only applicable when producer option is empty or not present.' ), 'inv_option': fields.selection([('01', 'Unknown'), ('02', 'Various'), (' ', ' ')], 'Sold to Option'), 'inv_company': fields.char( 'CompanyName', size=256, help='Only applicable when Sold to option is empty or not present.' ), 'inv_tax_id_no': fields.char( 'TaxIdentificationNumber', size=256, help='Only applicable when Sold to option is empty or not present.' ), 'inv_att_name': fields.char( 'AttentionName', size=256, help='Only applicable when Sold to option is empty or not present.' ), 'inv_address_id': fields.many2one( 'res.partner', 'Sold To Address', help='Only applicable when Sold to option is empty or not present.' ), 'blanket_begin_date': fields.date('Blanket Begin Date'), 'blanket_end_date': fields.date('Blanket End Date'), 'comm_code': fields.char( 'Commodity Code', size=256, ), 'exp_carrier': fields.char('ExportingCarrier', size=256), 'ship_company_code': fields.selection(_get_company_code, 'Ship Company', method=True, size=64), 'ship_charge': fields.float('Value', digits_compute=dp.get_precision('Account')) } _defaults = { 'address_validate': 'nonvalidate', 'comm_inv': False, 'cer_orig': False, 'nafta_cer_orig': False, 'sed': False, 'ship_state': 'draft', 'bill_shipping': 'shipper', 'ship_charge': 0.0 } def print_labels(self, cr, uid, ids, context=None): if not ids: return [] return { 'type': 'ir.actions.report.xml', 'report_name': 'multiple.label.print', 'datas': { 'model': 'stock.picking', 'id': ids and ids[0] or False, 'ids': ids, 'report_type': 'pdf' }, 'nodestroy': True } def print_packing_slips(self, cr, uid, ids, context=None): if not ids: return [] packages_ids = [] for package in self.browse(cr, uid, ids[0]).packages_ids: packages_ids.append(package.id) x = { 'type': 'ir.actions.report.xml', 'report_name': 'package.packing.slip.print', 'datas': { 'model': 'stock.packages', 'id': ids and ids[0] or False, 'ids': packages_ids, 'report_type': 'pdf' }, 'nodestroy': True } return x
class product_product(product_variant_osv): _inherit = "product.product" def init(self, cr): #For the first installation if you already have product in your database the name of the existing product will be empty, so we fill it cr.execute( "update product_product set name=name_template where name is null;" ) return True def unlink(self, cr, uid, ids, context=None): if context is None: context = {} context['unlink_from_product_product'] = True return super(product_product, self).unlink(cr, uid, ids, context) def build_product_name(self, cr, uid, ids, context=None): return self.build_product_field(cr, uid, ids, 'name', context=None) def build_product_field(self, cr, uid, ids, field, context=None): if context is None: context = {} def get_description_sale(product): return self.parse(cr, uid, product, product.product_tmpl_id.description_sale, context=context) def get_name(product): if context.get('variants_values', False): return (product.product_tmpl_id.name or '') + ' ' + ( context['variants_values'][product.id] or '') return (product.product_tmpl_id.name or '') + ' ' + (product.variants or '') context['is_multi_variants'] = True obj_lang = self.pool.get('res.lang') lang_ids = obj_lang.search(cr, uid, [('translatable', '=', True)], context=context) lang_code = [ x['code'] for x in obj_lang.read( cr, uid, lang_ids, ['code'], context=context) ] for code in lang_code: context['lang'] = code for product in self.browse(cr, uid, ids, context=context): new_field_value = eval( "get_" + field + "(product)") # TODO convert to safe_eval cur_field_value = safe_eval("product." + field, {'product': product}) if new_field_value != cur_field_value: self.write(cr, uid, product.id, {field: new_field_value}, context=context) return True def write(self, cr, uid, ids, vals, context=None): if isinstance(ids, (int, long)): ids = [ids] if context is None: context = {} res = super(product_product, self).write(cr, uid, ids, vals.copy(), context=context) ids_simple = self.search( cr, uid, [['id', 'in', ids], ['is_multi_variants', '=', False]], context=context) if not context.get('iamthechild', False) and ids_simple: vals_to_write = self.get_vals_to_write(vals) if vals_to_write: obj_tmpl = self.pool.get('product.template') ctx = context.copy() ctx['iamthechild'] = True tmpl_ids = obj_tmpl.search(cr, uid, [['variant_ids', 'in', ids_simple]]) obj_tmpl.write(cr, uid, tmpl_ids, vals_to_write, context=ctx) return res def create(self, cr, uid, vals, context=None): #TAKE CARE for inherits objects openerp will create firstly the product_template and after the product_product # and so the duplicated fields (duplicated field = field which are on the template and on the variant) will be on the product_template and not on the product_product #Also when a product is created the duplicated field are empty for the product.product, this is why the field name can not be a required field #This should be fix in the orm in the futur ids = super(product_product, self).create( cr, uid, vals.copy(), context=context ) #using vals.copy() if not the vals will be changed by calling the super method ####### write the value in the product_product if context is None: context = {} ctx = context.copy() ctx['iamthechild'] = True vals_to_write = self.get_vals_to_write(vals) if vals_to_write: self.write(cr, uid, ids, vals_to_write, context=ctx) return ids def parse(self, cr, uid, o, text, context=None): if not text: return '' vals = text.split('[_') description = '' for val in vals: if '_]' in val: sub_val = val.split('_]') try: description += (safe_eval(sub_val[0], { 'o': o, 'context': context }) or '') + sub_val[1] except: LOGGER.notifyChannel( 'product_variant_multi', netsvc.LOG_ERROR, "%s can't eval. Description is blank" % (sub_val[0])) description += '' else: description += val return description def generate_product_code(self, cr, uid, product_obj, code_generator, context=None): '''I wrote this stupid function to be able to inherit it in a custom module !''' return self.parse(cr, uid, product_obj, code_generator, context=context) def build_product_code_and_properties(self, cr, uid, ids, context=None): if context is None: context = {} for product in self.browse(cr, uid, ids, context=context): new_default_code = self.generate_product_code( cr, uid, product, product.product_tmpl_id.code_generator, context=context) current_values = { 'default_code': product.default_code, 'track_production': product.track_production, 'track_outgoing': product.track_outgoing, 'track_incoming': product.track_incoming, } new_values = { 'default_code': new_default_code, 'track_production': product.product_tmpl_id.variant_track_production, 'track_outgoing': product.product_tmpl_id.variant_track_outgoing, 'track_incoming': product.product_tmpl_id.variant_track_incoming, } if new_values != current_values: self.write(cr, uid, product.id, new_values, context=context) return True def product_ids_variant_changed(self, cr, uid, ids, res, context=None): '''it's a hook for product_variant_multi advanced''' return True def generate_variant_name(self, cr, uid, product_id, context=None): '''Do the generation of the variant name in a dedicated function, so that we can inherit this function to hack the code generation''' if context is None: context = {} product = self.browse(cr, uid, product_id, context=context) model = product.variant_model_name r = map( lambda dim: [ dim.dimension_id.sequence, self.parse(cr, uid, dim, model, context=context) ], product.dimension_value_ids) r.sort() r = [x[1] for x in r] new_variant_name = (product.variant_model_name_separator or '').join(r) return new_variant_name def build_variants_name(self, cr, uid, ids, context=None): for product in self.browse(cr, uid, ids, context=context): new_variant_name = self.generate_variant_name(cr, uid, product.id, context=context) if new_variant_name != product.variants: self.write(cr, uid, product.id, {'variants': new_variant_name}, context=context) return True def _check_dimension_values( self, cr, uid, ids ): # TODO: check that all dimension_types of the product_template have a corresponding dimension_value ?? for p in self.browse(cr, uid, ids, {}): buffer = [] for value in p.dimension_value_ids: buffer.append(value.dimension_id) unique_set = set(buffer) if len(unique_set) != len(buffer): return False return True def compute_product_dimension_extra_price(self, cr, uid, product_id, product_price_extra=False, dim_price_margin=False, dim_price_extra=False, context=None): if context is None: context = {} dimension_extra = 0.0 product = self.browse(cr, uid, product_id, context=context) for dim in product.dimension_value_ids: if product_price_extra and dim_price_margin and dim_price_extra: dimension_extra += safe_eval( 'product.' + product_price_extra, {'product': product}) * safe_eval( 'dim.' + dim_price_margin, {'dim': dim}) + safe_eval( 'dim.' + dim_price_extra, {'dim': dim}) elif not product_price_extra and not dim_price_margin and dim_price_extra: dimension_extra += safe_eval('dim.' + dim_price_extra, {'dim': dim}) elif product_price_extra and dim_price_margin and not dim_price_extra: dimension_extra += safe_eval('product.' + product_price_extra, {'product': product}) * safe_eval( 'dim.' + dim_price_margin, {'dim': dim}) elif product_price_extra and not dim_price_margin and dim_price_extra: dimension_extra += safe_eval('product.' + product_price_extra, {'product': product}) + safe_eval( 'dim.' + dim_price_extra, {'dim': dim}) if 'uom' in context: product_uom_obj = self.pool.get('product.uom') uom = product.uos_id or product.uom_id dimension_extra = product_uom_obj._compute_price( cr, uid, uom.id, dimension_extra, context['uom']) return dimension_extra def compute_dimension_extra_price(self, cr, uid, ids, result, product_price_extra=False, dim_price_margin=False, dim_price_extra=False, context=None): if context is None: context = {} for product in self.browse(cr, uid, ids, context=context): dimension_extra = self.compute_product_dimension_extra_price( cr, uid, product.id, product_price_extra=product_price_extra, dim_price_margin=dim_price_margin, dim_price_extra=dim_price_extra, context=context) result[product.id] += dimension_extra return result def price_get(self, cr, uid, ids, ptype='list_price', context=None): if context is None: context = {} result = super(product_product, self).price_get(cr, uid, ids, ptype, context=context) if ptype == 'list_price': #TODO check if the price_margin on the dimension is very usefull, maybe we will remove it result = self.compute_dimension_extra_price( cr, uid, ids, result, product_price_extra='price_extra', dim_price_margin='price_margin', dim_price_extra='price_extra', context=context) elif ptype == 'standard_price': result = self.compute_dimension_extra_price( cr, uid, ids, result, product_price_extra='cost_price_extra', dim_price_extra='cost_price_extra', context=context) return result def _product_lst_price(self, cr, uid, ids, name, arg, context=None): if context is None: context = {} result = super(product_product, self)._product_lst_price(cr, uid, ids, name, arg, context=context) result = self.compute_dimension_extra_price( cr, uid, ids, result, product_price_extra='price_extra', dim_price_margin='price_margin', dim_price_extra='price_extra', context=context) return result def copy(self, cr, uid, id, default=None, context=None): if default is None: default = {} default = default.copy() default.update({ 'variant_ids': False, }) return super(product_product, self).copy(cr, uid, id, default, context) def _product_compute_weight_volume(self, cr, uid, ids, fields, arg, context=None): result = {} # print 'compute', ids, fields, context for product in self.browse(cr, uid, ids, context=context): result[product.id] = {} result[product. id]['weight'] = product.weight + product.additional_weight result[product.id][ 'weight_net'] = product.weight_net + product.additional_weight_net result[product. id]['volume'] = product.volume + product.additional_volume return result _columns = { 'name': fields.char('Name', size=128, translate=True, select=True), 'dimension_value_ids': fields.many2many('product.variant.dimension.value', 'product_product_dimension_rel', 'product_id', 'dimension_id', 'Dimensions', domain="[('product_tmpl_id','=',product_tmpl_id)]"), 'cost_price_extra': fields.float('Purchase Extra Cost', digits_compute=dp.get_precision('Purchase Price')), 'lst_price': fields.function(_product_lst_price, method=True, type='float', string='List Price', digits_compute=dp.get_precision('Sale Price')), #the way the weight are implemented are not clean at all, we should redesign the module product form the addons in order to get something correclty. #indeed some field of the template have to be overwrited like weight, name, weight_net, volume. #in order to have a consitent api we should use the same field for getting the weight, now we have to use "weight" or "total_weight" not clean at all with external syncronization 'total_weight': fields.function(_product_compute_weight_volume, method=True, type='float', string='Gross weight', help="The gross weight in Kg.", multi='weight_volume'), 'total_weight_net': fields.function(_product_compute_weight_volume, method=True, type='float', string='Net weight', help="The net weight in Kg.", multi='weight_volume'), 'total_volume': fields.function(_product_compute_weight_volume, method=True, type='float', string='Volume', help="The volume in m3.", multi='weight_volume'), 'additional_weight': fields.float('Additional Gross weight', help="The additional gross weight in Kg."), 'additional_weight_net': fields.float('Additional Net weight', help="The additional net weight in Kg."), 'additional_volume': fields.float('Additional Volume', help="The additional volume in Kg."), } _constraints = [ (_check_dimension_values, 'Several dimension values for the same dimension type', ['dimension_value_ids']), ]
class member(osv.osv): '''会员信息设置''' _name = "member_management.member" _descripton = "会员信息" _rec_name = "member_no" def _compute_balance_and_points(self, cr, uid, ids, field_name, args, context): ''' 计算卡余额与积分 ''' ret = {} for record in self.browse(cr, uid, ids): sum_charge = sum_present_charge_fee = sum_consumption = sum_balance = 0.0 sum_points = 0 for charge in record.member_charge_ids: sum_charge += charge.charge_fee #充值金额合计 sum_present_charge_fee += charge.present_charge_fee #充值赠送金额合计 for consumption in record.member_consumption_ids: sum_consumption += consumption.paid_fee sum_points += consumption.points ret[record.id] = { 'balance': sum_charge + sum_present_charge_fee - sum_consumption, #TODO 计算积分点数时,需要减去积分兑换中扣除的积分 'points': sum_points, } return ret _columns = { "member_no": fields.char("member_no", size=30, readonly=True, select=True, help="会员编号,由系统自动生成", required=True), "name": fields.char("name", size=20, required=True, help="会员名称"), "member_class_id": fields.many2one("member_management.member_class", "member_class_id", select=True, required=True, help="会员等级"), "member_card_no": fields.char("card_id", size=30, select=True, required=True, help="会员卡号,卡具上印刷的编号"), "photo": fields.binary("photo", filter=".jpg,.png,.bmp"), "card_fee": fields.float("make_fee", digits=(10, 2), help="制卡费用,默认取会员等级中的制卡费用"), "up_card_fee": fields.float("up_card_fee", digits=(10, 2), help="补卡费用,默认取会员等级中的制卡费用"), "begin_datetime": fields.datetime("begin_datetime", readonly=True, required=True, help="发卡时间"), "valid_date": fields.date("valid_date", help="卡有效期"), "overdraft_fee": fields.float("overdraft_fee", digits=(10, 2), help="可透支额度"), "card_password": fields.char("card_password", size=20, required=True, help="卡密码,不可为空"), "phone": fields.char("phone", size=20, help="联系电话"), "birthday": fields.date("birthday", help="联系电话"), "sex": fields.selection(helper.sexes_for_select, "sex", help="性别"), "id_type": fields.selection(helper.id_types_for_select, "id_type", help="证件类型"), "id_no": fields.char("id_no", size=30, help="证件号码"), "v_no": fields.char("v_no", size=30, help="车牌号码"), "qq": fields.char("qq", size=30, help="QQ号码"), "email": fields.char("email", size=30, help="邮件地址"), "company": fields.char("company", size=30, help="工作单位"), "address": fields.char("address", size=60, help="地址"), "member_charge_ids": fields.one2many('member_management.member_charge', 'member_id', help="会员充值记录"), "member_consumption_ids": fields.one2many('member_management.member_consumption', 'member_id', help="会员消费记录"), "balance": fields.function( _compute_balance_and_points, type='float', multi="compute_fields", string="balance", digits_compute=dp.get_precision('member_management_fee'), help="卡余额"), "points": fields.function(_compute_balance_and_points, type='integer', multi="cimpute_fields", string="points", help="卡积分"), "card_state": fields.boolean("state"), "active": fields.boolean("active"), 'room_fee_discount': fields.related('member_class_id', 'room_fee_discount', type='float', string='房费折扣'), 'drinks_fee_discount': fields.related('member_class_id', 'drinks_fee_discount', type='float', string='酒水费折扣'), } _defaults = { "active": True, "card_state": True, "card_fee": 0.0, 'up_card_fee': 0.0, "overdraft_fee": 0.0, 'begin_datetime': fields.datetime.now, 'member_no': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get( cr, uid, 'member_management.member'), 'balance': 0.0, } def calculate_casher_shift_report(self, cr, uid, start_datetime, end_datetime): ''' 计算会员卡业务发生情况 :param start_datetime datetime 业务起始时间 :param end_datetime datetime 业务结束时间 :return dict 'member_card_count' 新会员数量 'member_card_fee' 会员卡销售金额 'member_charge_fee' 会员充值金额 ''' pool = self.pool #计算新办会员卡数量 ids = self.search( cr, uid, [('begin_datetime', '>=', helper.strftime(start_datetime)), ('begin_datetime', '<=', helper.strftime(end_datetime))]) member_card_count = len(ids) member_card_fee = 0.0 for r in self.browse(cr, uid, ids): member_card_fee += r.card_fee + r.up_card_fee #计算会员充值金额 charge_fee = 0.0 charge_ids = pool.get('member_management.member_charge').search(cr,uid,[('bill_datetime','>=',helper.strftime(start_datetime)), \ ('bill_datetime','<=',helper.strftime(end_datetime))]) for r in pool.get('member_management.member_charge').browse( cr, uid, charge_ids): charge_fee += r.charge_fee return { 'member_card_count': member_card_count, 'new_member_card_fee': member_card_fee, 'member_charge_fee': charge_fee, }
class account_invoice(osv.osv): _inherit = "account.invoice" _name = "account.invoice" def _amount_all_3(self, cr, uid, ids, name, args, context=None): res = {} amount_untaxed = 0 for invoice in self.browse(cr, uid, ids, context=context): res[invoice.id] = { 'base_doce_iva': 0.00, 'base_cero_iva': 0.00, 'vat_doce_subtotal': 0.00, 'vat_cero_subtotal': 0.00, 'total_iva': 0.00, 'total_with_vat': 0.00, 'total_to_withhold': 0.00, } for line in invoice.invoice_line: amount_untaxed += line.price_subtotal for line in invoice.tax_line: if line.amount > 0: if line.type_ec == 'iva' and line.amount > 0: res[invoice.id]['base_doce_iva'] += line.base res[invoice.id]['vat_doce_subtotal'] += line.amount if line.type_ec == 'iva': res[invoice.id]['total_iva'] += line.amount else: if line.type_ec == 'iva' and line.amount == 0 and not ( 'RETENCION' in line.name): res[invoice.id]['base_cero_iva'] += line.base res[invoice.id]['vat_cero_subtotal'] += line.amount res[invoice.id]['total_to_withhold'] += line.amount res[invoice.id]['total_with_vat'] = amount_untaxed + res[invoice.id][ 'vat_cero_subtotal'] + res[invoice.id]['vat_doce_subtotal'] return res _columns = { #TODO hacer obligatorio el campo name que almacenara el numero de la factura 'internal_number': fields.char( 'Invoice Number', size=17, readonly=False, help= "Unique number of the invoice, computed automatically when the invoice is created." ), #'supplier_invoice_number': fields.char('Supplier Invoice Number', size=18, help="The reference of this invoice as provided by the supplier.", readonly=True, states={'draft':[('readonly',False)]}), # 'shop_id':fields.many2one('sale.shop', 'Shop', readonly=True, states={'draft':[('readonly',False)]}), 'printer_id': fields.many2one('sri.printer.point', 'Printer Point', required=False, ondelete='restrict'), 'invoice_address': fields.char( "Invoice address", help= "Invoice address as in VAT document, saved in invoice only not in partner" ), 'invoice_phone': fields.char( "Invoice phone", help= "Invoice phone as in VAT document, saved in invoice only not in partner" ), # invoice_rectification_id la factura a la cual se esta rectificando, invoice_rectification_ids las facturas que rectifican a la actual 'invoice_rectification_id': fields.many2one( 'account.invoice', 'Modified Invoice', readonly=True, states={'draft': [('readonly', False)]}, help="This field sets the invoice that is being modified", track_visibility='always'), 'invoice_rectification_ids': fields.one2many( 'account.invoice', 'invoice_rectification_id', 'Modified Invoices', readonly=True, help= "This field sets the invoices those are modifying the actual invoice", track_visibility='always'), 'base_doce_iva': fields.function(_amount_all_3, digits_compute=dp.get_precision('Account'), string='IVA 12 Base', store=True, multi='all1'), 'base_cero_iva': fields.function(_amount_all_3, method=True, digits_compute=dp.get_precision('Account'), string='IVA 0 Base', store=True, multi='all1'), 'vat_doce_subtotal': fields.function(_amount_all_3, method=True, digits_compute=dp.get_precision('Account'), string='IVA 12 %', store=True, multi='all1'), 'vat_cero_subtotal': fields.function(_amount_all_3, method=True, digits_compute=dp.get_precision('Account'), string='IVA 0 %', store=True, multi='all1'), 'total_iva': fields.function(_amount_all_3, method=True, digits_compute=dp.get_precision('Account'), string='Total IVA', store=True, multi='all1'), 'total_with_vat': fields.function(_amount_all_3, method=True, digits_compute=dp.get_precision('Account'), string='Total with taxes', store=True, multi='all1'), 'total_to_withhold': fields.function(_amount_all_3, method=True, digits_compute=dp.get_precision('Account'), string='Total to withhold', store=True, multi='all1'), } RE_PREFIXED_INVOICE = re.compile('^\d+-\d+-\d+$') def __init__(self, pool, cr): """ Durante la inicialización del modelo correremos un SQL para borrar una constraint de SQL que de alguna manera nunca fue borrada, y no tenemos en este OpenERP un mecanismo que gestione migraciones, por lo que este proceso deberíamos hacerlo manualmente. Cuando el módulo se inicializa (se construye) toma dos valores: el pool para poder obtener otros objetos, y el cr para ejecutar consultas de postgresql. Tomando ese cursor ejecutamos -LUEGO de llamar al super- una sentencia SQL de borrado de constraint: ALTER TABLE account_invoice DROP CONSTRAINT IF EXISTS account_invoice_number_uniq (http://www.postgresql.org/docs/9.1/static/sql-altertable.html). El nombre de la tabla está dado a falta de un nombre preconfigurado para la tabla en este modelo, por el nombre de la propia clase (account_invoice). El nombre de la constraint viene dado por lo reportado en FDU-636. Se envuelve todo en un try ... finally ya que, si no existen las tablas al momento de crear este modulo, entonces no deberia importarnos el hecho de que esta consulta sql falle por una tabla que no exista. :param pool: :param cr: :return: """ super(account_invoice, self).__init__(pool, cr) try: cr.execute( 'ALTER TABLE account_invoice DROP CONSTRAINT IF EXISTS account_invoice_number_uniq' ) pass finally: pass #ignoramos cualquier error. def _check_number_invoice(self, cr, uid, ids, context=None): res = True def unlink(self, cr, uid, ids, context=None): """ Allow delete a invoice in draft state """ invoices = self.read(cr, uid, ids, ['state'], context=context) unlink_ids = [] for inv in invoices: if inv['state'] == 'draft': unlink_ids.append(inv['id']) # write False in the invoice number, this allow eliminate the invoice self.write(cr, uid, inv['id'], {'internal_number': False}) else: raise osv.except_osv( _('Invalid action!'), _('You can delete Invoice in state Draft')) return super(account_invoice, self).unlink(cr, uid, unlink_ids, context) def copy(self, cr, uid, id, default=None, context=None): """ Copia una factura pero le pone el prefijo en lugar de copiar tambien el numero interno. si el numero interno no tiene forma de xxx-xxx-xxxxxx entonces no copia prefijo, pone una cadena vacia """ obj = self.browse(cr, uid, id) default = default or {} internal_number = obj.internal_number if (self.RE_PREFIXED_INVOICE.match(internal_number)): default['internal_number'] = '-'.join( internal_number.split('-')[0:2] + ['']) else: default['internal_number'] = '' default.update({'name': ''}) return super(account_invoice, self).copy(cr, uid, id, default, context) def onchange_internal_number(self, cr, uid, ids, internal_number, context=None): value = {} if not internal_number: return {'value': value} internal_number = str(internal_number) number_split = str.split(internal_number, "-") if len(number_split) == 3 and number_split[2] != "": if len(number_split[2]) < 17: #require auto complete pos = 0 fill = 9 - len(number_split[2]) for car in number_split[2]: if car != '0': break pos = pos + 1 number_split[2] = number_split[ 2][:pos] + "0" * fill + number_split[2][pos:] value.update({ 'internal_number': number_split[0] + "-" + number_split[1] + "-" + number_split[2], }) return {'value': value} def onchange_date_invoice(self, cr, uid, ids, date_invoice, context=None): ''' Asigna un periodo fiscal acorde a la fecha ''' res = {} warning = {} periodo = "" if not date_invoice: return {} obj_period = self.pool.get('account.period') period_id = obj_period.search(cr, uid, [('date_start', '<=', date_invoice), ('date_stop', '>=', date_invoice)]) if not period_id: warning = { 'title': _('Warning!'), 'message': _('No existe un período contable para esta fecha. There is no date for this accounting period.' ) } else: period = obj_period.browse(cr, uid, period_id, context=context) periodo = period.pop().id res = { 'value': { 'period_id': periodo }, 'warning': warning, 'domain': {} } return res def _default_printer_point(self, cr, uid, context=None): ''' Si el usuario tiene configurado un printer point lo selecciona Caso contrario intenta con el 001-001 ''' printer_point_id = False #intenta el printer_point del usuario user_obj = self.pool.get('res.users') printer = user_obj.browse(cr, uid, uid).printer_id if printer: printer_point_id = printer.id return printer_point_id #si no esta definido usamos el primero que exista, usuallmente sera el 001-001 printer_point_obj = self.pool.get('sri.printer.point') printer_point_id = printer_point_obj.search(cr, uid, [], limit=1) if printer_point_id: return printer_point_id[0] return None def _suggested_internal_number(self, cr, uid, printer_id=None, type=None, company_id=None, context=None): '''Numero de factura sugerida para facturas de venta y compra, depende del punto de impresion Puede ser redefinida posteriormente por ejemplo para numeracion automatica ''' if context is None: context = {} if 'type' in context: type = context['type'] if not printer_id: printer_id = self._default_printer_point(cr, uid, context) number = False #por ejemplo para facturas de tipo hr_advance # Se corrige las devoluciones y las facturas con sus prefijos correspondientes if type in ['out_invoice', 'out_refund']: number = '001-001-' printer = self.pool.get('sri.printer.point').browse( cr, uid, printer_id, context=context) if printer.prefix: number = printer.prefix else: number = printer.shop_id.number + "-" + printer.name + "-" if type in ['in_invoice', 'in_refund']: number = '001-001-' return number def _get_internal_number_by_sequence(self, cr, uid, obj_inv, context=None): """ Generates, for the given object and number, a valid autogenerated number (if it can do and neither the current user has, """ #we must ensure there's an available printer point by invoice, by user, or the first one printer_id = obj_inv.printer_id or self.pool.get('res.users').browse( cr, uid, uid).printer_id if not printer_id: ppobj = self.pool.get('sri.printer.point') pprecs_limit1 = ppobj.search(cr, uid, [], limit=1) pprec_first = pprecs_limit1[0] if pprecs_limit1 else False printer_id = ppobj.browse(cr, uid, pprec_first) if pprec_first else False #if no invoice is found, we return the number as-is if not printer_id: return obj_inv.number #we get the document type to fetch from the printer. #if the invoice has a wrong type, the passed type is # None, which will cause the number to be returned. document_type = { 'out_invoice': 'invoice', 'out_refund': 'refund' }.get(obj_inv.type) return self.pool.get('sri.printer.point').get_next_sequence_number( cr, uid, printer_id, document_type, obj_inv.number, context) def action_number(self, cr, uid, ids, context=None): """ This method allows the usage of custom sequentials for the printer point. It stores the internal_number from the number field, passed to an internal check from the current printer point """ super(account_invoice, self).action_number(cr, uid, ids, context) for obj_inv in self.browse(cr, uid, ids, context=context): number = self._get_internal_number_by_sequence( cr, uid, obj_inv, context) self.write(cr, uid, ids, { 'internal_number': number, 'name': number, 'number': number }) return True def _default_internal_number(self, cr, uid, context=None): '''Numero de factura sugerida para facturas de venta y compra, depende del punto de impresion Puede ser redefinida posteriormente por ejemplo para numeracion automatica ''' number = '' type = '' printer_id = None if context is None: context = {} if context.has_key('type'): type = context['type'] if context.has_key('printer_id'): printer_id = context['printer_id'] if not printer_id: printer_id = self._default_printer_point(cr, uid, context) if printer_id and type: number = self._suggested_internal_number(cr, uid, printer_id, type, None, context) return number def _default_date_invoice(self, cr, uid, context=None): '''Fecha por defecto es hoy ''' #TODO: Incluir el calculo de zona horaria #TODO: Colocar esta funcion en el default return str(lambda *a: time.strftime('%Y-%m-%d'), ) _defaults = { 'printer_id': _default_printer_point, 'internal_number': _default_internal_number, 'date_invoice': lambda *a: time.strftime('%Y-%m-%d'), } def _prepare_invoice_header(self, cr, uid, partner_id, type, inv_date=None, printer_id=None, context=None): """Retorna los valores ecuatorianos para el header de una factura Puede ser usado en ordenes de compra, venta, proyectos, facturacion desde bodegas, etc @partner_id es un objeto partner @type es el tipo de factura, ej. out_invoice @inv_date es la fecha prevista de la factura, si no se provee se asume hoy """ if context is None: context = {} invoice_vals = {} partner_obj = self.pool.get('res.partner') invoice_address = partner_obj.get_company_address(cr, uid, partner_id) invoice_phone = partner_obj.get_company_phone(cr, uid, partner_id) inv_obj = self.pool.get('account.invoice') printer_id = printer_id or inv_obj._default_printer_point(cr, uid, uid) internal_number = '' if printer_id: internal_number = inv_obj._suggested_internal_number( cr, uid, printer_id, type, context) invoice_vals.update({ 'invoice_address': invoice_address or '', 'invoice_phone': invoice_phone or '', 'internal_number': internal_number or '', 'printer_id': printer_id, 'date_invoice': time.strftime('%Y-%m-%d') }) return invoice_vals
class product_template(osv.osv): _name = "product.template" _description = "Product Template" def _calc_seller(self, cr, uid, ids, fields, arg, context=None): result = {} for product in self.browse(cr, uid, ids, context=context): for field in fields: result[product.id] = {field: False} result[product.id]['seller_delay'] = 1 if product.seller_ids: partner_list = sorted([ (partner_id.sequence, partner_id) for partner_id in product.seller_ids if partner_id and isinstance(partner_id.sequence, (int, long)) ]) main_supplier = partner_list and partner_list[ 0] and partner_list[0][1] or False result[product.id][ 'seller_delay'] = main_supplier and main_supplier.delay or 1 result[product.id][ 'seller_qty'] = main_supplier and main_supplier.qty or 0.0 result[product.id][ 'seller_id'] = main_supplier and main_supplier.name.id or False return result _columns = { 'name': fields.char('Name', size=128, required=True, translate=True, select=True), 'product_manager': fields.many2one('res.users', 'Product Manager', help="This is use as task responsible"), 'description': fields.text('Description', translate=True), 'description_purchase': fields.text('Purchase Description', translate=True), 'description_sale': fields.text('Sale Description', translate=True), 'type': fields.selection( [('product', 'Stockable Product'), ('consu', 'Consumable'), ('service', 'Service')], 'Product Type', required=True, help= "Will change the way procurements are processed. Consumables are stockable products with infinite stock, or for use when you have no inventory management in the system." ), 'supply_method': fields.selection( [('produce', 'Produce'), ('buy', 'Buy')], 'Supply method', required=True, help= "Produce will generate production order or tasks, according to the product type. Purchase will trigger purchase orders when requested." ), 'sale_delay': fields.float( 'Customer Lead Time', help= "This is the average delay in days between the confirmation of the customer order and the delivery of the finished products. It's the time you promise to your customers." ), 'produce_delay': fields.float( 'Manufacturing Lead Time', help= "Average delay in days to produce this product. This is only for the production order and, if it is a multi-level bill of material, it's only for the level of this product. Different lead times will be summed for all levels and purchase orders." ), 'procure_method': fields.selection( [('make_to_stock', 'Make to Stock'), ('make_to_order', 'Make to Order')], 'Procurement Method', required=True, help= "'Make to Stock': When needed, take from the stock or wait until re-supplying. 'Make to Order': When needed, purchase or produce for the procurement request." ), 'rental': fields.boolean('Can be Rent'), 'categ_id': fields.many2one('product.category', 'Category', required=True, change_default=True, domain="[('type','=','normal')]", help="Select category for the current product"), 'list_price': fields.float( 'Sale Price', digits_compute=dp.get_precision('Sale Price'), help= "Base price for computing the customer price. Sometimes called the catalog price." ), 'standard_price': fields.float( 'Cost Price', required=True, digits_compute=dp.get_precision('Purchase Price'), help= "Product's cost for accounting stock valuation. It is the base price for the supplier price." ), 'volume': fields.float('Volume', help="The volume in m3."), 'weight': fields.float('Gross weight', help="The gross weight in Kg."), 'weight_net': fields.float('Net weight', help="The net weight in Kg."), 'cost_method': fields.selection( [('standard', 'Standard Price'), ('average', 'Average Price')], 'Costing Method', required=True, help= "Standard Price: the cost price is fixed and recomputed periodically (usually at the end of the year), Average Price: the cost price is recomputed at each reception of products." ), 'warranty': fields.float('Warranty (months)'), 'sale_ok': fields.boolean( 'Can be Sold', help= "Determines if the product can be visible in the list of product within a selection from a sale order line." ), 'purchase_ok': fields.boolean( 'Can be Purchased', help= "Determine if the product is visible in the list of products within a selection from a purchase order line." ), 'state': fields.selection( [('', ''), ('draft', 'In Development'), ('sellable', 'Normal'), ('end', 'End of Lifecycle'), ('obsolete', 'Obsolete')], 'Status', help="Tells the user if he can use the product or not."), 'uom_id': fields.many2one( 'product.uom', 'Default Unit Of Measure', required=True, help="Default Unit of Measure used for all stock operation."), 'uom_po_id': fields.many2one( 'product.uom', 'Purchase Unit of Measure', required=True, help= "Default Unit of Measure used for purchase orders. It must be in the same category than the default unit of measure." ), 'uos_id': fields.many2one( 'product.uom', 'Unit of Sale', help= 'Used by companies that manage two units of measure: invoicing and inventory management. For example, in food industries, you will manage a stock of ham but invoice in Kg. Keep empty to use the default UOM.' ), 'uos_coeff': fields.float('UOM -> UOS Coeff', digits=(16, 4), help='Coefficient to convert UOM to UOS\n' ' uos = uom * coeff'), 'mes_type': fields.selection((('fixed', 'Fixed'), ('variable', 'Variable')), 'Measure Type', required=True), 'seller_delay': fields.function( _calc_seller, method=True, type='integer', string='Supplier Lead Time', multi="seller_delay", help= "This is the average delay in days between the purchase order confirmation and the reception of goods for this product and for the default supplier. It is used by the scheduler to order requests based on reordering delays." ), 'seller_qty': fields.function( _calc_seller, method=True, type='float', string='Supplier Quantity', multi="seller_qty", help="This is minimum quantity to purchase from Main Supplier."), 'seller_id': fields.function( _calc_seller, method=True, type='many2one', relation="res.partner", string='Main Supplier', help="Main Supplier who has highest priority in Supplier List.", multi="seller_id"), 'seller_ids': fields.one2many('product.supplierinfo', 'product_id', 'Partners'), 'loc_rack': fields.char('Rack', size=16), 'loc_row': fields.char('Row', size=16), 'loc_case': fields.char('Case', size=16), 'company_id': fields.many2one('res.company', 'Company', select=1), } def _get_uom_id(self, cr, uid, *args): cr.execute('select id from product_uom order by id limit 1') res = cr.fetchone() return res and res[0] or False def _default_category(self, cr, uid, context=None): if context is None: context = {} if 'categ_id' in context and context['categ_id']: return context['categ_id'] md = self.pool.get('ir.model.data') res = md.get_object_reference(cr, uid, 'product', 'cat0') or False return res and res[1] or False def onchange_uom(self, cursor, user, ids, uom_id, uom_po_id): if uom_id: return {'value': {'uom_po_id': uom_id}} return False _defaults = { 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get( cr, uid, 'product.template', context=c), 'type': lambda *a: 'product', 'list_price': lambda *a: 1, 'cost_method': lambda *a: 'standard', 'supply_method': lambda *a: 'buy', 'standard_price': lambda *a: 1, 'sale_ok': lambda *a: 1, 'sale_delay': lambda *a: 7, 'produce_delay': lambda *a: 1, 'purchase_ok': lambda *a: 1, 'procure_method': lambda *a: 'make_to_stock', 'uom_id': _get_uom_id, 'uom_po_id': _get_uom_id, 'uos_coeff': lambda *a: 1.0, 'mes_type': lambda *a: 'fixed', 'categ_id': _default_category, 'type': lambda *a: 'consu', } def _check_uom(self, cursor, user, ids, context=None): for product in self.browse(cursor, user, ids, context=context): if product.uom_id.category_id.id <> product.uom_po_id.category_id.id: return False return True def _check_uos(self, cursor, user, ids, context=None): for product in self.browse(cursor, user, ids, context=context): if product.uos_id \ and product.uos_id.category_id.id \ == product.uom_id.category_id.id: return False return True _constraints = [ (_check_uom, 'Error: The default UOM and the purchase UOM must be in the same category.', ['uom_id']), ] def name_get(self, cr, user, ids, context=None): if context is None: context = {} if 'partner_id' in context: pass return super(product_template, self).name_get(cr, user, ids, context)
class product_product(orm.Model): """ Inherit Product in order to add an "Bom Stock" field """ _inherit = 'product.product' # def _get_prefered_supplier(self, cr, uid, ids, field_name, args, context): # res = {} # for line in self.browse(cr, uid, ids, context): # res[line.id] = line.seller_ids and line.seller_ids[0].name.id or False # return res def __init__(self, cr, uid): super(product_product, self).__init__(cr, uid) self.product_cost_cache = {} def _compute_purchase_price(self, cr, uid, ids, product_uom=None, bom_properties=None, context=None): ''' Compute the purchase price, taking into account sub products and routing ''' context = context or self.pool['res.users'].context_get(cr, uid) bom_properties = bom_properties or [] user = self.pool['res.users'].browse(cr, uid, uid, context) bom_obj = self.pool['mrp.bom'] uom_obj = self.pool['product.uom'] res = {} ids = ids or [] for product in self.browse(cr, uid, ids, context): # print(u'{product.id}: {product.name}'.format(product=product)) bom_id = bom_obj._bom_find(cr, uid, product.id, product_uom=None, properties=bom_properties) if bom_id: sub_bom_ids = bom_obj.search(cr, uid, [('bom_id', '=', bom_id)], context=context) sub_products = bom_obj.browse(cr, uid, sub_bom_ids, context) price = 0. for sub_product in sub_products: if sub_product.product_id.id == product.id: error = "Product '{product.name}' (id: {product.id}) is referenced to itself".format( product=product) _logger.error(error) continue # std_price = sub_product.standard_price if ENABLE_CACHE: if sub_product.product_id.id in self.product_cost_cache: std_price = self.product_cost_cache[ sub_product.product_id.id] else: std_price = sub_product.product_id.cost_price self.product_cost_cache[ sub_product.product_id.id] = std_price else: std_price = sub_product.product_id.cost_price qty = uom_obj._compute_qty( cr, uid, from_uom_id=sub_product.product_uom.id, qty=sub_product.product_qty, to_uom_id=sub_product.product_id.uom_po_id.id) price += std_price * qty # if sub_product.routing_id: # for wline in sub_product.routing_id.workcenter_lines: # wc = wline.workcenter_id # cycle = wline.cycle_nbr # # hour = (wc.time_start + wc.time_stop + cycle * wc.time_cycle) * (wc.time_efficiency or 1.0) # price += wc.costs_cycle * cycle + wc.costs_hour * wline.hour_nbr if sub_products: # Don't use browse when we already have it bom = sub_products[0].bom_id else: bom = bom_obj.browse(cr, uid, bom_id, context) if bom.routing_id: for wline in bom.routing_id.workcenter_lines: wc = wline.workcenter_id cycle = wline.cycle_nbr # hour = (wc.time_start + wc.time_stop + cycle * wc.time_cycle) * (wc.time_efficiency or 1.0) price += wc.costs_cycle * cycle + wc.costs_hour * wline.hour_nbr price /= bom.product_qty price = uom_obj._compute_price(cr, uid, bom.product_uom.id, price, bom.product_id.uom_id.id) res[product.id] = price else: # no BoM: use standard_price # use standard_price if no supplier indicated if product.id in self.product_cost_cache and ENABLE_CACHE: res[product.id] = self.product_cost_cache[product.id] continue if product.prefered_supplier: pricelist = product.prefered_supplier.property_product_pricelist_purchase or False ctx = {'date': time.strftime(DEFAULT_SERVER_DATE_FORMAT)} price = self.pool['product.pricelist'].price_get( cr, uid, [pricelist.id], product.id, 1, context=ctx)[pricelist.id] or 0 price_subtotal = 0.0 if pricelist: from_currency = pricelist.currency_id.id to_currency = user.company_id.currency_id.id price_subtotal = self.pool['res.currency'].compute( cr, uid, from_currency_id=from_currency, to_currency_id=to_currency, from_amount=price, context=context) res[product.id] = price_subtotal or price else: res[product.id] = product.standard_price if ENABLE_CACHE: self.product_cost_cache[product.id] = res[product.id] continue return res def get_cost_field(self, cr, uid, ids, context=None): start_time = datetime.now() context = context or self.pool['res.users'].context_get(cr, uid) end_time = datetime.now() duration_seconds = (end_time - start_time) duration = '{sec}'.format(sec=duration_seconds) _logger.info(u'get_cost_field get in {duration} for {id}'.format( duration=duration, id=ids)) res = self._cost_price(cr, uid, ids, '', [], context) return res def _cost_price(self, cr, uid, ids, field_name, arg, context=None): start_time = datetime.now() # _logger.error( # u'START _cost_price for {ids} and {field}'.format(ids=ids, field=field_name)) context = context or self.pool['res.users'].context_get(cr, uid) product_uom = context.get('product_uom') bom_properties = context.get('properties') res = self._compute_purchase_price(cr, uid, ids, product_uom, bom_properties, context) end_time = datetime.now() duration_seconds = (end_time - start_time) duration = '{sec}'.format(sec=duration_seconds) _logger.info( u'_cost_price get in {duration}'.format(duration=duration)) return res def _kit_filter(self, cr, uid, obj, name, args, context): if not args: return [] bom_obj = self.pool['mrp.bom'] for search in args: if search[0] == 'is_kit': if search[2]: bom_ids = bom_obj.search(cr, uid, [('bom_id', '=', False)], context=context) if bom_ids: res = [ bom.product_id.id for bom in bom_obj.browse( cr, uid, bom_ids, context) ] return [('id', 'in', res)] else: return [('id', 'in', [])] return [] def _is_kit(self, cr, uid, ids, product_uom=None, bom_properties=None, context=None): if not len(ids): return [] ''' Show if have or not a bom ''' start_time = datetime.now() context = context or self.pool['res.users'].context_get(cr, uid) bom_properties = bom_properties or [] bom_obj = self.pool['mrp.bom'] res = {} ids = ids or [] for product in self.browse(cr, uid, ids, context): bom_id = bom_obj._bom_find(cr, uid, product.id, product_uom=None, properties=bom_properties) # cr.execute("""SELECT id FROM mrp_bom WHERE product_id={product_id}""".format(product_id=product.id)) # bom_id = cr.fetchall() if not bom_id: res[product.id] = False else: res[product.id] = True end_time = datetime.now() duration_seconds = (end_time - start_time) duration = '{sec}'.format(sec=duration_seconds) _logger.info(u'IS KIT get in {duration}'.format(duration=duration)) return res """ Inherit Product in order to add an "Bom Stock" field """ def _bom_stock_mapping(self, cr, uid, context=None): return { 'real': 'qty_available', 'virtual': 'virtual_available', 'immediately': 'immediately_usable_qty' } def _compute_bom_stock(self, cr, uid, product, quantities, company, context=None): context = context or self.pool['res.users'].context_get(cr, uid) bom_obj = self.pool['mrp.bom'] uom_obj = self.pool['product.uom'] mapping = self._bom_stock_mapping(cr, uid, context=context) stock_field = mapping[company.ref_stock] product_qty = quantities.get(stock_field, 0.0) # find a bom on the product bom_id = bom_obj._bom_find(cr, uid, product.id, product.uom_id.id, properties=[]) if bom_id: prod_min_quantities = [] bom = bom_obj.browse(cr, uid, bom_id, context=context) if bom.bom_lines: stop_compute_bom = False # Compute stock qty of each product used in the BoM and # get the minimal number of items we can produce with them for line in bom.bom_lines: prod_min_quantity = 0.0 bom_qty = line.product_id[ stock_field] # expressed in product UOM # the reference stock of the component must be greater # than the quantity of components required to # build the bom line_product_qty = uom_obj._compute_qty_obj( cr, uid, line.product_uom, line.product_qty, line.product_id.uom_id, context=context) if line_product_qty and (bom_qty > line_product_qty): prod_min_quantity = bom_qty / line_product_qty # line.product_qty is always > 0 else: # if one product has not enough stock, # we do not need to compute next lines # because the final quantity will be 0.0 in any case stop_compute_bom = True prod_min_quantities.append(prod_min_quantity) if stop_compute_bom: break produced_qty = uom_obj._compute_qty_obj(cr, uid, bom.product_uom, bom.product_qty, bom.product_id.uom_id, context=context) if prod_min_quantities: product_qty += min(prod_min_quantities) * produced_qty else: product_qty += produced_qty return product_qty def _product_available(self, cr, uid, ids, field_names=[], arg=False, context=None): # We need available, virtual or immediately usable # quantity which is selected from company to compute Bom stock Value # so we add them in the calculation. context = context or self.pool['res.users'].context_get(cr, uid) start_time = datetime.now() user_obj = self.pool['res.users'] comp_obj = self.pool['res.company'] if 'bom_stock' in field_names: field_names.append('qty_available') field_names.append('immediately_usable_qty') field_names.append('virtual_available') res = super(product_product, self)._product_available(cr, uid, ids, field_names, arg, context) if 'bom_stock' in field_names: company = user_obj.browse(cr, uid, uid, context=context).company_id if not company: company_id = comp_obj.search(cr, uid, [], context=context)[0] company = comp_obj.browse(cr, uid, company_id, context=context) for product_id, stock_qty in res.iteritems(): product = self.browse(cr, uid, product_id, context=context) res[product_id]['bom_stock'] = self._compute_bom_stock( cr, uid, product, stock_qty, company, context=context) end_time = datetime.now() duration_seconds = (end_time - start_time) duration = '{sec}'.format(sec=duration_seconds) _logger.info( u'_product_available get in {duration}'.format(duration=duration)) return res def _get_boms(self, cr, uid, ids, field_name, arg, context): result = {} for product_id in ids: result[product_id] = self.pool['mrp.bom'].search( cr, uid, [('product_id', '=', product_id), ('bom_id', '=', False)], context=context) return result def price_get(self, cr, uid, ids, ptype='list_price', context=None): start_time = datetime.now() context = context or self.pool['res.users'].context_get(cr, uid) if 'currency_id' in context: pricetype_obj = self.pool['product.price.type'] price_type_id = pricetype_obj.search(cr, uid, [('field', '=', ptype)], context=context)[0] price_type_currency_id = pricetype_obj.browse( cr, uid, price_type_id, context).currency_id.id res = {} product_uom_obj = self.pool['product.uom'] for product in self.browse(cr, uid, ids, context=context): res[product.id] = product[ptype] or 0.0 if ptype == 'standard_price' and product.is_kit: res[product.id] = product.cost_price or product.standard_price if ptype == 'list_price': res[product.id] = ( res[product.id] * (product.price_margin or 1.0)) + product.price_extra if 'uom' in context: uom = product.uom_id or product.uos_id res[product.id] = product_uom_obj._compute_price( cr, uid, uom.id, res[product.id], context['uom']) # Convert from price_type currency to asked one if 'currency_id' in context: # Take the price_type currency from the product field # This is right cause a field cannot be in more than one currency res[product.id] = self.pool['res.currency'].compute( cr, uid, price_type_currency_id, context['currency_id'], res[product.id], context=context) end_time = datetime.now() duration_seconds = (end_time - start_time) duration = '{sec}'.format(sec=duration_seconds) _logger.info(u'price_get get in {duration}'.format(duration=duration)) return res _columns = { 'date_inventory': fields.function(lambda *a, **k: {}, method=True, type='date', string="Date Inventory"), 'cost_price': fields.function(_cost_price, method=True, string=_('Cost Price (incl. BoM)'), digits_compute=dp.get_precision('Purchase Price'), help="The cost price is the standard price or, if the product has a bom, " "the sum of all standard price of its components. it take also care of the " "bom costing like cost per cylce."), 'prefered_supplier': fields.related('seller_ids', 'name', type='many2one', relation='res.partner', string='Prefered Supplier'), 'is_kit': fields.function(_is_kit, fnct_search=_kit_filter, method=True, type="boolean", string="Kit"), 'bom_lines': fields.function(_get_boms, relation='mrp.bom', string='Boms', type='one2many', method=True), 'qty_available': fields.function( _product_available, multi='qty_available', type='float', digits_compute=dp.get_precision('Product UoM'), string='Quantity On Hand', help="Current quantity of products.\n" "In a context with a single Stock Location, this includes " "goods stored at this Location, or any of its children.\n" "In a context with a single Warehouse, this includes " "goods stored in the Stock Location of this Warehouse, " "or any " "of its children.\n" "In a context with a single Shop, this includes goods " "stored in the Stock Location of the Warehouse of this Shop, " "or any of its children.\n" "Otherwise, this includes goods stored in any Stock Location " "typed as 'internal'."), 'virtual_available': fields.function( _product_available, multi='qty_available', type='float', digits_compute=dp.get_precision('Product UoM'), string='Quantity Available', help="Forecast quantity (computed as Quantity On Hand " "- Outgoing + Incoming)\n" "In a context with a single Stock Location, this includes " "goods stored at this Location, or any of its children.\n" "In a context with a single Warehouse, this includes " "goods stored in the Stock Location of this Warehouse, " "or any " "of its children.\n" "In a context with a single Shop, this includes goods " "stored in the Stock Location of the Warehouse of this Shop, " "or any of its children.\n" "Otherwise, this includes goods stored in any Stock Location " "typed as 'internal'."), 'incoming_qty': fields.function( _product_available, multi='qty_available', type='float', digits_compute=dp.get_precision('Product UoM'), string='Incoming', help="Quantity of products that are planned to arrive.\n" "In a context with a single Stock Location, this includes " "goods arriving to this Location, or any of its children.\n" "In a context with a single Warehouse, this includes " "goods arriving to the Stock Location of this Warehouse, or " "any of its children.\n" "In a context with a single Shop, this includes goods " "arriving to the Stock Location of the Warehouse of this " "Shop, or any of its children.\n" "Otherwise, this includes goods arriving to any Stock " "Location typed as 'internal'."), 'outgoing_qty': fields.function( _product_available, multi='qty_available', type='float', digits_compute=dp.get_precision('Product UoM'), string='Outgoing', help="Quantity of products that are planned to leave.\n" "In a context with a single Stock Location, this includes " "goods leaving from this Location, or any of its children.\n" "In a context with a single Warehouse, this includes " "goods leaving from the Stock Location of this Warehouse, or " "any of its children.\n" "In a context with a single Shop, this includes goods " "leaving from the Stock Location of the Warehouse of this " "Shop, or any of its children.\n" "Otherwise, this includes goods leaving from any Stock " "Location typed as 'internal'."), 'immediately_usable_qty': fields.function( _product_available, digits_compute=dp.get_precision('Product UoM'), type='float', string='Immediately Usable', multi='qty_available', help="Quantity of products really available for sale." \ "Computed as: Quantity On Hand - Outgoing."), 'bom_stock': fields.function( _product_available, digits_compute=dp.get_precision('Product UoM'), type='float', string='Bill of Materials Stock', help="Quantities of products based on Bill of Materials, " "useful to know how much of this " "product you could produce. " "Computed as:\n " "Reference stock of this product + " "how much could I produce of this product with the BoM" "Components", multi='qty_available'), } def copy(self, cr, uid, product_id, default=None, context=None): """Copies the product and the BoM of the product""" context = context or self.pool['res.users'].context_get(cr, uid) copy_id = super(product_product, self).copy(cr, uid, product_id, default, context) bom_obj = self.pool['mrp.bom'] bom_ids = bom_obj.search(cr, uid, [('product_id', '=', product_id), ('bom_id', '=', False)], context=context) for bom_id in bom_ids: bom_obj.copy(cr, uid, bom_id, {'product_id': copy_id}, context=context) return copy_id def update_product_bom_price(self, cr, uid, ids, context=None): """ This Function is call by scheduler. """ context = context or self.pool['res.users'].context_get(cr, uid) for product in self.browse(cr, uid, ids, context): product.write({'standard_price': product.cost_price}) return True def update_bom_price(self, cr, uid, context=None): """ This Function is call by scheduler. """ context = context or self.pool['res.users'].context_get(cr, uid) # search product with kit product_ids = self.search(cr, uid, [('is_kit', '=', True)], context=context) for product in self.browse(cr, uid, product_ids, context): product.write({'standard_price': product.cost_price}) return True def write(self, cr, uid, ids, vals, context=None): context = context or self.pool['res.users'].context_get(cr, uid) if not isinstance(ids, (list, tuple)): ids = [ids] res = super(product_product, self).write(cr, uid, ids, vals, context) if ENABLE_CACHE: if 'standard_price' in vals: changed_product = ids bom_obj = self.pool['mrp.bom'] bom_ids = bom_obj.search(cr, uid, [('product_id', 'in', ids)], context=context) for bom in bom_obj.browse(cr, uid, bom_ids, context): bom_parent = bom.bom_id while bom_parent: changed_product.append(bom_parent.product_id.id) bom_parent = bom_parent.bom_id for product_id in changed_product: if product_id in self.product_cost_cache: del self.product_cost_cache[product_id] return res
class product_uom(osv.osv): _name = 'product.uom' _description = 'Product Unit of Measure' def _compute_factor_inv(self, factor): return factor and round(1.0 / factor, 6) or 0.0 def _factor_inv(self, cursor, user, ids, name, arg, context=None): res = {} for uom in self.browse(cursor, user, ids, context=context): res[uom.id] = self._compute_factor_inv(uom.factor) return res def _factor_inv_write(self, cursor, user, id, name, value, arg, context=None): return self.write(cursor, user, id, {'factor': self._compute_factor_inv(value)}, context=context) def create(self, cr, uid, data, context=None): if 'factor_inv' in data: if data['factor_inv'] <> 1: data['factor'] = self._compute_factor_inv(data['factor_inv']) del (data['factor_inv']) return super(product_uom, self).create(cr, uid, data, context) _columns = { 'name': fields.char('Name', size=64, required=True, translate=True), 'category_id': fields.many2one('product.uom.categ', 'UoM Category', required=True, ondelete='cascade', help="Quantity conversions may happen automatically between Units of Measure in the same category, according to their respective ratios."), 'factor': fields.float('Ratio', required=True,digits=(12, 12), help='How many times this UoM is smaller than the reference UoM in this category:\n'\ '1 * (reference unit) = ratio * (this unit)'), 'factor_inv': fields.function(_factor_inv, digits_compute=dp.get_precision('Product UoM'), fnct_inv=_factor_inv_write, method=True, string='Ratio', help='How many times this UoM is bigger than the reference UoM in this category:\n'\ '1 * (this unit) = ratio * (reference unit)', required=True), 'rounding': fields.float('Rounding Precision', digits_compute=dp.get_precision('Product UoM'), required=True, help="The computed quantity will be a multiple of this value. "\ "Use 1.0 for a UoM that cannot be further split, such as a piece."), 'active': fields.boolean('Active', help="By unchecking the active field you can disable a unit of measure without deleting it."), 'uom_type': fields.selection([('bigger','Bigger than the reference UoM'), ('reference','Reference UoM for this category'), ('smaller','Smaller than the reference UoM')],'UoM Type', required=1), } _defaults = { 'active': 1, 'rounding': 0.01, 'uom_type': 'reference', } _sql_constraints = [ ('factor_gt_zero', 'CHECK (factor!=0)', 'The conversion ratio for a unit of measure cannot be 0!'), ] def _compute_qty(self, cr, uid, from_uom_id, qty, to_uom_id=False): if not from_uom_id or not qty or not to_uom_id: return qty uoms = self.browse(cr, uid, [from_uom_id, to_uom_id]) if uoms[0].id == from_uom_id: from_unit, to_unit = uoms[0], uoms[-1] else: from_unit, to_unit = uoms[-1], uoms[0] return self._compute_qty_obj(cr, uid, from_unit, qty, to_unit) def _compute_qty_obj(self, cr, uid, from_unit, qty, to_unit, context=None): if context is None: context = {} if from_unit.category_id.id <> to_unit.category_id.id: if context.get('raise-exception', True): raise osv.except_osv( _('Error !'), _('Conversion from Product UoM %s to Default UoM %s is not possible as they both belong to different Category!.' ) % ( from_unit.name, to_unit.name, )) else: return qty amount = qty / from_unit.factor if to_unit: amount = rounding(amount * to_unit.factor, to_unit.rounding) return amount def _compute_price(self, cr, uid, from_uom_id, price, to_uom_id=False): if not from_uom_id or not price or not to_uom_id: return price uoms = self.browse(cr, uid, [from_uom_id, to_uom_id]) if uoms[0].id == from_uom_id: from_unit, to_unit = uoms[0], uoms[-1] else: from_unit, to_unit = uoms[-1], uoms[0] if from_unit.category_id.id <> to_unit.category_id.id: return price amount = price * from_unit.factor if to_uom_id: amount = amount / to_unit.factor return amount def onchange_type(self, cursor, user, ids, value): if value == 'reference': return {'value': {'factor': 1, 'factor_inv': 1}} return {}
class stock_move(osv.osv): _inherit = "stock.move" # override copy and copy_data method to prevent copying landing cost when creating returns def copy(self, cr, uid, id, default=None, context=None): if default is None: default = {} default['landing_costs_line_ids'] = [] res = super(stock_move, self).copy(cr, uid, id, default, context) return res def copy_data(self, cr, uid, id, default=None, context=None): res = super(stock_move, self).copy_data(cr, uid, id, default, context) if res.get('landing_costs_line_ids', False): res['landing_costs_line_ids'] = [] if res.get('price_unit_without_costs', False): res['price_unit'] = res['price_unit_without_costs'] res['price_unit_without_costs'] = False return res # per amount landing costs amount def _landing_costs_per_value(self, cr, uid, ids, name, args, context): if not ids: return {} result = {} for line in self.browse(cr, uid, ids): per_value = 0.0 if line.landing_costs_line_ids: for costs in line.landing_costs_line_ids: if costs.distribution_type == 'per_value': per_value += costs.amount result[line.id] = per_value return result # per unit landing costs amount def _landing_costs_per_unit(self, cr, uid, ids, name, args, context): if not ids: return {} result = {} for line in self.browse(cr, uid, ids): per_unit = 0.0 if line.landing_costs_line_ids: for costs in line.landing_costs_line_ids: if costs.distribution_type == 'per_unit': per_unit += costs.amount result[line.id] = per_unit return result # stock move quantity for cost calculation def _landing_costs_base_quantity(self, cr, uid, ids, name, args, context): if not ids: return {} result = {} base_quantity = 0.0 for line in self.browse(cr, uid, ids): if line.product_id.landing_cost_calculate: base_quantity = line.product_qty result[line.id] = base_quantity return result # stock move amount for costs calculation def _landing_costs_base_amount(self, cr, uid, ids, name, args, context): if not ids: return {} result = {} base_amount = 0.0 for line in self.browse(cr, uid, ids): if line.product_id.landing_cost_calculate: base_amount = line.price_unit * line.product_qty result[line.id] = base_amount return result def _landing_costs_price_unit_with_costs(self, cr, uid, ids, name, args, context): if not ids: return {} result = {} for line in self.browse(cr, uid, ids): if line.price_unit_without_costs and line.price_unit_without_costs <> 0.0: price_unit_with_costs = line.price_unit_without_costs else: price_unit_with_costs = line.price_unit or 0.0 if line.product_id.landing_cost_calculate: if line.picking_id and (line.picking_id.landing_costs_per_value > 0.0 or line.picking_id.landing_costs_per_unit > 0.0): # landing costs - picking - amount if line.picking_id.landing_costs_base_amount > 0.0 and line.product_qty > 0.0: price_unit_with_costs += ((line.picking_id.landing_costs_per_value / line.picking_id.landing_costs_base_amount) * (line.price_unit * line.product_qty)) / line.product_qty # landing costs - picking - quantity if line.product_qty > 0.0: price_unit_with_costs += line.picking_id.landing_costs_per_unit / line.picking_id.landing_costs_base_quantity # landing costs - move if line.product_qty > 0.0 and (line.landing_costs_per_value > 0.0 or line.landing_costs_per_unit > 0.0): price_unit_with_costs += (line.landing_costs_per_value + line.landing_costs_per_unit) / line.product_qty if line.price_unit != price_unit_with_costs: self.write(cr, uid, [line.id], {'price_unit': price_unit_with_costs}) result[line.id] = price_unit_with_costs return result _columns = { 'price_unit_without_costs': fields.float('', digits_compute=dp.get_precision('Product Price'), ), 'price_unit_with_costs': fields.function(_landing_costs_price_unit_with_costs, digits_compute=dp.get_precision('Product Price'), ), 'landing_costs_line_ids': fields.one2many('purchase.landing.cost.position', 'move_line_id', 'Landing Costs'), 'landing_costs_per_value': fields.function(_landing_costs_per_value, digits_compute=dp.get_precision('Product Price'), string='Landing Costs Amount Per Value For Average Price'), 'landing_costs_per_unit': fields.function(_landing_costs_per_unit, digits_compute=dp.get_precision('Product Price'), string='Landing Costs Amount Per Unit For Average Price'), 'landing_costs_base_quantity': fields.function(_landing_costs_base_quantity, digits_compute=dp.get_precision('Product Price'), string='Stock Move Quantity For Per Unit Calculation'), 'landing_costs_base_amount': fields.function(_landing_costs_base_amount, digits_compute=dp.get_precision('Product Price'), string='Stock Move For Per Value Calculation'), }
class account_transfer(osv.osv): def _get_balance(self, src_journal, dst_journal, company): src_balance = dst_balance = 0.0 #import pdb; pdb.set_trace() if src_journal.default_credit_account_id.id == src_journal.default_debit_account_id.id: if not src_journal.currency or company.currency_id.id == src_journal.currency.id: src_balance = src_journal.default_credit_account_id.balance else: src_balance = src_journal.default_credit_account_id.foreign_balance else: if not src_journal.currency or company.currency_id.id == src_journal.currency.id: src_balance = src_journal.default_debit_account_id.balance - src_journal.default_credit_account_id.balance else: src_balance = src_journal.default_debit_account_id.foreign_balance - src_journal.default_credit_account_id.foreign_balance if dst_journal.default_credit_account_id.id == dst_journal.default_debit_account_id.id: if not dst_journal.currency or company.currency_id.id == dst_journal.currency.id: dst_balance = dst_journal.default_credit_account_id.balance else: dst_balance = dst_journal.default_credit_account_id.foreign_balance else: if not dst_journal.currency or company.currency_id.id == dst_journal.currency.id: dst_balance = dst_journal.default_debit_account_id.balance - dst_journal.default_credit_account_id.balance else: dst_balance = dst_journal.default_debit_account_id.foreign_balance - dst_journal.default_credit_account_id.foreign_balance return (src_balance, dst_balance) def _balance(self, cr, uid, ids, field_name, arg, context=None): res = {} for trans in self.browse(cr, uid, ids, context=context): src_balance, dst_balance = self._get_balance( trans.src_journal_id, trans.dst_journal_id, trans.company_id) exchange = False if trans.dst_journal_id.currency.id != trans.src_journal_id.currency.id: exchange = True res[trans.id] = { 'src_balance': src_balance, 'dst_balance': dst_balance, 'exchange': exchange, 'exchange_inv': (trans.exchange_rate and 1.0 / trans.exchange_rate or 0.0) } return res STATE_SELECTION = [ ('draft', 'Draft'), ('confirm', 'Confirm'), ('done', 'Done'), ('cancel', 'Cancel'), ] _columns = { 'company_id': fields.many2one('res.company', 'Company', required=True, readonly=True, states={'draft': [('readonly', False)]}), 'name': fields.char('Number', size=32, required=True, readonly=True, states={'draft': [('readonly', False)]}), 'date': fields.date('Date', required=True, readonly=True, states={'draft': [('readonly', False)]}), 'origin': fields.char('Origin', size=128, readonly=True, states={'draft': [('readonly', False)]}, help="Origin Document"), 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True, states={'draft': [('readonly', False)]}), 'voucher_ids': fields.one2many('account.voucher', 'transfer_id', string='Payments', readonly=True, states={ 'draft': [('readonly', False)], 'confirm': [('readonly', False)] }), 'src_journal_id': fields.many2one('account.journal', 'Source Journal', required=True, domain=[('type', 'in', ['cash', 'bank'])], select=True, readonly=True, states={'draft': [('readonly', False)]}), 'src_partner_id': fields.many2one('res.partner', 'Source Partner', select=True), 'src_balance': fields.function( _balance, digits_compute=dp.get_precision('Account'), string='Current Source Balance', type='float', readonly=True, multi='balance', help="Include all account moves in draft and confirmed state"), 'src_amount': fields.float('Source Amount', required=True, readonly=True, states={'draft': [('readonly', False)]}), 'src_have_partner': fields.related('src_journal_id', 'have_partner', type='boolean', string='Have Partner', readonly=True), 'dst_journal_id': fields.many2one('account.journal', 'Destinity Journal', required=True, domain=[('type', 'in', ['cash', 'bank'])], select=True, readonly=True, states={'draft': [('readonly', False)]}), 'dst_partner_id': fields.many2one('res.partner', 'Destinity Partner', select=True), 'dst_balance': fields.function( _balance, digits_compute=dp.get_precision('Account'), string='Current Destinity Balance', type='float', readonly=True, multi='balance', help="Include all account moves in draft and confirmed state"), 'dst_amount': fields.float('Destinity Amount', required=True, readonly=True, states={'draft': [('readonly', False)]}), 'dst_have_partner': fields.related('dst_journal_id', 'have_partner', type='boolean', string='Have Partner', readonly=True), 'exchange_rate': fields.float('Exchange Rate', digits_compute=dp.get_precision('Exchange'), readonly=True, states={'draft': [('readonly', False)]}), 'exchange': fields.function(_balance, string='Have Exchange', type='boolean', readonly=True, multi='balance'), 'exchange_inv': fields.function(_balance, string='1 / Exchange Rate', type='float', digits_compute=dp.get_precision('Exchange'), readonly=True, multi='balance'), 'adjust_move': fields.many2one( 'account.move', 'Adjust Move', readonly=True, help="Adjust move usually by difference in the money exchange"), 'state': fields.selection(STATE_SELECTION, string='State', readonly=True), } _defaults = { 'name': lambda s, cr, u, c: s.pool.get('ir.sequence').get( cr, u, 'account.transfer'), 'company_id': lambda s, cr, u, c: s.pool.get('res.users').browse(cr, u, u).company_id .id, 'date': lambda *a: time.strftime('%Y-%m-%d'), 'exchange_rate': 1.0, 'exchange_inv': 1.0, 'state': 'draft', } _sql_constraints = [('name_unique', 'unique(company_id,name)', _('The number must be unique!'))] _name = 'account.transfer' _inherit = ['mail.thread', 'ir.needaction_mixin'] _description = 'Account Cash and Bank Transfer' _order = 'name desc' def unlink(self, cr, uid, ids, context=None): for trans in self.browse(cr, uid, ids, context=context): if trans.state not in ('draft'): raise osv.except_osv( _('User Error!'), _('You cannot delete a not draft transfer "%s"') % trans.name) return super(account_transfer, self).unlink(cr, uid, ids, context=context) def copy(self, cr, uid, id, defaults, context=None): defaults['name'] = self.pool.get('ir.sequence').get( cr, uid, 'account.transfer') defaults['voucher_ids'] = [] return super(account_transfer, self).copy(cr, uid, id, defaults, context=context) def onchange_amount(self, cr, uid, ids, field, src_amount, dst_amount, exchange_rate): res = {'value': {}} if field == 'src_amount': res['value']['src_amount'] = src_amount res['value']['dst_amount'] = src_amount * exchange_rate res['value']['exchange_rate'] = exchange_rate res['value'][ 'exchange_inv'] = exchange_rate and 1.0 / exchange_rate or 0.0 elif field == 'dst_amount': res['value'][ 'src_amount'] = exchange_rate and dst_amount / exchange_rate or 0.0 res['value']['dst_amount'] = dst_amount res['value']['exchange_rate'] = exchange_rate res['value'][ 'exchange_inv'] = exchange_rate and 1.0 / exchange_rate or 0.0 elif field == 'exchange_rate': res['value']['src_amount'] = src_amount res['value']['dst_amount'] = src_amount * exchange_rate res['value']['exchange_rate'] = exchange_rate res['value'][ 'exchange_inv'] = exchange_rate and 1.0 / exchange_rate or 0.0 return res def onchange_journal(self, cr, uid, ids, src_journal_id, dst_journal_id, date, exchange_rate, src_amount): res = {'value': {}} if not (src_journal_id and dst_journal_id): return res src_journal = self.pool.get('account.journal').browse( cr, uid, src_journal_id) dst_journal = self.pool.get('account.journal').browse( cr, uid, dst_journal_id) res['value']['src_balance'], res['value'][ 'dst_balance'] = self._get_balance(src_journal, dst_journal, src_journal.company_id) res['value']['exchange'] = (src_journal.currency.id != dst_journal.currency.id) res['value']['src_have_partner'], res['value'][ 'dst_have_partner'] = src_journal.have_partner, dst_journal.have_partner res['value']['exchange_rate'] = exchange_rate if res['value']['exchange']: res['value']['exchange_rate'] = ( src_journal.currency and src_journal.currency.rate or src_journal.company_id.currency_id.rate) and ( (dst_journal.currency and dst_journal.currency.rate or dst_journal.company_id.currency_id.rate) / (src_journal.currency and src_journal.currency.rate or src_journal.company_id.currency_id.rate)) or 0.0 else: res['value']['exchange_rate'] = 1.0 res['value']['exchange_inv'] = res['value']['exchange_rate'] and ( 1.0 / res['value']['exchange_rate']) or 0.0 res['value']['dst_amount'] = res['value']['exchange_rate'] * src_amount return res def action_confirm(self, cr, uid, ids, context=None): voucher_obj = self.pool.get('account.voucher') for trans in self.browse(cr, uid, ids, context=context): sval = {} dval = {} sval['transfer_id'] = trans.id dval['transfer_id'] = trans.id sval['type'] = 'transfer' dval['type'] = 'transfer' sval['company_id'] = trans.company_id.id dval['company_id'] = trans.company_id.id sval['reference'] = trans.name + str(trans.origin and (' - ' + trans.origin) or '') dval['reference'] = trans.name + str(trans.origin and (' - ' + trans.origin) or '') sval['line_ids'] = [(0, 0, {})] dval['line_ids'] = [(0, 0, {})] sval['line_ids'][0][2][ 'account_analytic_id'] = trans.account_analytic_id and trans.account_analytic_id.id or 0 dval['line_ids'][0][2][ 'account_analytic_id'] = trans.account_analytic_id and trans.account_analytic_id.id or 0 sval['line_ids'][0][2]['name'] = trans.origin dval['line_ids'][0][2]['name'] = trans.origin sval['journal_id'] = trans.src_journal_id.id dval['journal_id'] = trans.dst_journal_id.id sval[ 'account_id'] = trans.src_journal_id.default_credit_account_id.id dval[ 'account_id'] = trans.dst_journal_id.default_debit_account_id.id sval[ 'payment_rate'] = trans.src_journal_id.currency.id and trans.company_id.currency_id.id <> trans.src_journal_id.currency.id and trans.exchange_rate or 1.0 dval[ 'payment_rate'] = trans.dst_journal_id.currency.id and trans.company_id.currency_id.id <> trans.dst_journal_id.currency.id and trans.exchange_inv or 1.0 sval[ 'payment_rate_currency_id'] = trans.dst_journal_id.currency.id or trans.company_id.currency_id.id sval[ 'payment_rate_currency_id'] = trans.src_journal_id.currency.id or trans.company_id.currency_id.id #import pdb; pdb.set_trace() sval['line_ids'][0][2]['amount'] = sval[ 'amount'] = trans.src_amount dval['line_ids'][0][2]['amount'] = dval[ 'amount'] = trans.dst_amount sval['line_ids'][0][2]['type'] = 'dr' dval['line_ids'][0][2]['type'] = 'cr' sval['line_ids'][0][2][ 'account_id'] = trans.dst_journal_id.default_debit_account_id.id if trans.src_partner_id.id ^ trans.dst_partner_id.id: sval[ 'partner_id'] = trans.src_have_partner and trans.src_partner_id.id or trans.dst_partner_id.id else: sval[ 'partner_id'] = trans.src_have_partner and trans.src_partner_id.id or trans.company_id.partner_id.id dval[ 'partner_id'] = trans.dst_have_partner and trans.dst_partner_id.id or trans.company_id.partner_id.id sval['line_ids'][0][2]['account_id'] = dval['line_ids'][0][2][ 'account_id'] = trans.src_journal_id.account_transit.id voucher_obj.create(cr, uid, dval, context=context) voucher_obj.create(cr, uid, sval, context=context) return self.write(cr, uid, ids, {'state': 'confirm'}, context=context) def action_done(self, cr, uid, ids, context=None): voucher_obj = self.pool.get('account.voucher') move_obj = self.pool.get('account.move') for trans in self.browse(cr, uid, ids, context=context): paid_amount = [] for voucher in trans.voucher_ids: voucher.state == 'draft' and voucher_obj.proforma_voucher( cr, uid, [voucher.id], context=context) sign = (voucher.journal_id.id == trans.src_journal_id.id) and 1 or -1 paid_amount.append( sign * voucher_obj._paid_amount_in_company_currency( cr, uid, [voucher.id], '', '')[voucher.id]) #paid_amount.append(sign * voucher.paid_amount_in_company_currency) sum_amount = sum(paid_amount) if len(paid_amount) > 1 and sum_amount != 0.0: periods = self.pool.get('account.period').find(cr, uid) move = {} move['journal_id'] = trans.dst_journal_id.id move['period_id'] = periods and periods[0] or False move['ref'] = trans.name + str(trans.origin and (' - ' + trans.origin) or '') move['date'] = trans.date move['line_id'] = [(0, 0, {}), (0, 0, {})] move['line_id'][0][2]['name'] = trans.name move['line_id'][1][2]['name'] = trans.name if sum_amount > 0: move['line_id'][0][2][ 'account_id'] = trans.dst_journal_id.default_debit_account_id.id move['line_id'][1][2][ 'account_id'] = trans.src_journal_id.account_transit.id #trans.company_id.income_currency_exchange_account_id.id move['line_id'][0][2]['debit'] = sum_amount move['line_id'][1][2]['credit'] = sum_amount else: move['line_id'][0][2][ 'account_id'] = trans.dst_journal_id.default_credit_account_id.id move['line_id'][1][2][ 'account_id'] = trans.src_journal_id.account_transit.id #trans.company_id.expense_currency_exchange_account_id.id move['line_id'][1][2]['debit'] = -1 * sum_amount move['line_id'][0][2]['credit'] = -1 * sum_amount move_id = move_obj.create(cr, uid, move, context=context) self.write(cr, uid, [trans.id], {'adjust_move': move_id}, context=context) return self.write(cr, uid, ids, {'state': 'done'}, context=context) def action_cancel(self, cr, uid, ids, context=None): voucher_obj = self.pool.get('account.voucher') move_obj = self.pool.get('account.move') #import pdb; pdb.set_trace() for trans in self.browse(cr, uid, ids, context=context): for voucher in trans.voucher_ids: voucher_obj.unlink(cr, uid, [voucher.id], context=context) trans.adjust_move and move_obj.unlink( cr, uid, [trans.adjust_move.id], context=context) return self.write(cr, uid, ids, {'state': 'cancel'}, context=context)
class account_analytic_line_plan(osv.osv): _name = 'account.analytic.line.plan' _description = 'Analytic planning line' def _get_company_currency(self, cr, uid, context=None): """ Returns the default company currency """ if context is None: context = {} company_obj = self.pool.get('res.company') company_id = self.pool.get('res.company')._company_default_get( cr, uid, 'account.analytic.line', context=context) company = company_obj.browse(cr, uid, company_id, context=context) return company.currency_id and company.currency_id.id or False _columns = { 'name': fields.char('Activity description', size=256, required=True), 'date': fields.date('Date', required=True, select=True), 'amount': fields.float('Amount', required=True, help='Calculated by multiplying the quantity ' 'and the price given in the Product\'s cost ' 'price. Always expressed in the company main ' 'currency.', digits_compute=dp.get_precision('Account')), 'unit_amount': fields.float('Quantity', help='Specifies the amount of quantity to count.'), 'amount_currency': fields.float( 'Amount Currency', help="The amount expressed in an optional other currency."), 'currency_id': fields.many2one('res.currency', 'Currency'), 'account_id': fields.many2one('account.analytic.account', 'Analytic Account', required=True, ondelete='cascade', select=True, domain=[('type', '<>', 'view')]), 'user_id': fields.many2one('res.users', 'User'), 'company_id': fields.related('account_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True), 'product_uom_id': fields.many2one('product.uom', 'UoM'), 'product_id': fields.many2one('product.product', 'Product'), 'general_account_id': fields.many2one('account.account', 'General Account', required=False, ondelete='restrict'), 'journal_id': fields.many2one('account.analytic.plan.journal', 'Planning Analytic Journal', required=True, ondelete='restrict', select=True), 'code': fields.char('Code', size=8), 'ref': fields.char('Ref.', size=64), 'notes': fields.text('Notes'), 'version_id': fields.many2one('account.analytic.plan.version', 'Planning Version', required=True, ondelete='cascade'), } _defaults = { 'date': lambda *a: time.strftime('%Y-%m-%d'), 'company_id': lambda self, cr, uid, c: self.pool.get('res.company'). _company_default_get(cr, uid, 'account.analytic.line', context=c), 'currency_id': _get_company_currency, 'amount': 0.00, 'journal_id': lambda self, cr, uid, context: context['journal_id'] if context and 'journal_id' in context else None, 'version_id': lambda s, cr, uid, c: s.pool.get('account.analytic.plan.version'). search(cr, uid, [('default_plan', '=', True)], context=None), } _order = 'date desc' def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False): if context is None: context = {} if context.get('from_date', False): args.append(['date', '>=', context['from_date']]) if context.get('to_date', False): args.append(['date', '<=', context['to_date']]) return super(account_analytic_line_plan, self).search(cr, uid, args, offset, limit, order, context=context, count=count) def _check_company(self, cr, uid, ids, context=None): lines = self.browse(cr, uid, ids, context=context) for l in lines: if l.move_id and not l.account_id.company_id.id == l.move_id.account_id.company_id.id: return False return True def on_change_amount_currency(self, cr, uid, id, amount_currency, currency_id, company_id, context=None): res = {} res['value'] = {} if context is None: context = {} currency_obj = self.pool.get('res.currency') company_obj = self.pool.get('res.company') company = company_obj.browse(cr, uid, company_id, context=context) company_currency_id = company.currency_id.id if amount_currency: amount_company_currency = currency_obj.compute(cr, uid, currency_id, company_currency_id, amount_currency, context=context) else: amount_company_currency = 0.0 res['value'].update({ 'amount': amount_company_currency, }) return res def on_change_unit_amount(self, cr, uid, id, prod_id, quantity, currency_id, company_id, unit=False, journal_id=False, context=None): res = {} if context is None: context = {} product_obj = self.pool.get('product.product') analytic_journal_obj = self.pool.get('account.analytic.plan.journal') product_price_type_obj = self.pool.get('product.price.type') prod = False if prod_id: prod = product_obj.browse(cr, uid, prod_id, context=context) res['value'] = {} if not journal_id: j_ids = analytic_journal_obj.search(cr, uid, [('type', '=', 'purchase')]) journal_id = j_ids and j_ids[0] or False if not journal_id or not prod_id: return res journal = analytic_journal_obj.browse(cr, uid, journal_id, context=context) if journal.type != 'sale' and prod: a = prod.product_tmpl_id.property_account_expense.id if not a: a = prod.categ_id.property_account_expense_categ.id if not a: raise osv.except_osv( _('Error !'), _('There is no expense account defined ' 'for this product: "%s" (id:%d)') % ( prod.name, prod.id, )) else: a = prod.product_tmpl_id.property_account_income.id if not a: a = prod.categ_id.property_account_income_categ.id if not a: raise osv.except_osv( _('Error !'), _('There is no income account defined ' 'for this product: "%s" (id:%d)') % ( prod.name, prod_id, )) flag = False # Compute based on pricetype product_price_type_ids = product_price_type_obj.search( cr, uid, [('field', '=', 'standard_price')], context=context) pricetype = product_price_type_obj.browse(cr, uid, product_price_type_ids, context=context)[0] if journal_id: if journal.type == 'sale': product_price_type_ids = product_price_type_obj.search( cr, uid, [('field', '=', 'list_price')], context) if product_price_type_ids: pricetype = product_price_type_obj.browse( cr, uid, product_price_type_ids, context=context)[0] # Take the company currency as the reference one if pricetype.field == 'list_price': flag = True ctx = context.copy() if unit: # price_get() will respect a 'uom' in its context, in order # to return a default price for those units ctx['uom'] = unit amount_unit = prod.price_get(pricetype.field, context=ctx)[prod.id] prec = self.pool.get('decimal.precision').precision_get( cr, uid, 'Account') amount = amount_unit * quantity or 1.0 result = round(amount, prec) if not flag: if journal.type != 'sale': result *= -1 res = self.on_change_amount_currency(cr, uid, id, result, currency_id, company_id, context) res['value'].update({ 'amount_currency': result, 'general_account_id': a, }) return res def on_change_product_id(self, cr, uid, id, prod_id, quantity, currency_id, company_id, unit=False, journal_id=False, context=None): res = self.on_change_unit_amount(cr, uid, id, prod_id, quantity, currency_id, company_id, unit, journal_id, context) prod = self.pool.get('product.product').browse(cr, uid, prod_id, context=context) prod_uom_po = prod.uom_po_id.id res['value'].update({ 'product_uom_id': prod_uom_po, }) return res def view_header_get(self, cr, user, view_id, view_type, context=None): if context is None: context = {} if context.get('account_id', False): # account_id in context may also be pointing to an account.account.id cr.execute('select name from account_analytic_account where id=%s', (context['account_id'], )) res = cr.fetchone() if res: res = _('Entries: ') + (res[0] or '') return res return False
class tcv_label_request_print_prn_export(osv.osv_memory): _name = 'tcv.label.request.print.prn.export' _description = '' _sections = ('<header>', '<body>', '<nextlabel>', '<footer>') ##------------------------------------------------------------------------- def load_label_template(self, label_template): actual_key = '' res = {} for line in label_template.splitlines(True): line_tmp = line[:-2] if line_tmp in self._sections: actual_key = line_tmp res.update({actual_key: ''}) elif actual_key: res[actual_key] = line if res[actual_key] == '' else '%s%s' % ( res[actual_key], line) return res def create_labels(self, label_list, label_template, params=None): params = params or {} res = label_template['<header>'] for label in label_list: body = '' for l in label_template['<body>'].splitlines(True): label_number = '%s>6%s' % ( label[:-1], label[-1]) if len(label) > 6 else label params.update({ 'label_number': label_number, 'label_number2': label, }) t = l % params body += t res = ''.join([res, body]) if label != label_list[-1]: res = ''.join((res, label_template['<nextlabel>'])) res = ''.join((res, label_template['<footer>'])) return res ##--------------------------------------------------------- function fields _columns = { 'name': fields.char('Filename', 64, readonly=True), 'prn_file': fields.binary('PRN file', readonly=True, filters='*.prn', help="PRN file name"), 'label_start': fields.char('Label start', size=16, required=False, readonly=True), 'label_end': fields.char('Label end', size=16, required=False, readonly=True), 'product_id': fields.many2one('product.product', 'Product', ondelete='restrict'), 'block_ref': fields.char('block_ref', size=128, required=False, readonly=False), 'price_1': fields.float('price_1', digits_compute=dp.get_precision('Account')), 'tax_1': fields.float('tax_1', digits_compute=dp.get_precision('Account')), 'price_2': fields.float('price_2', digits_compute=dp.get_precision('Account')), 'label_date': fields.char('label_date', size=128, required=False, readonly=False), 'label_template_id': fields.many2one('tcv.label.template', 'Gangsaw label template', required=True, readonly=False, ondelete='restrict', help="Default label template for gangsaw's labels"), 'company_id': fields.many2one('res.company', 'Company', required=True, readonly=True, ondelete='restrict'), 'loaded': fields.boolean('loaded'), } _defaults = { 'label_start': lambda *a: 0, 'label_end': lambda *a: 0, 'company_id': lambda self, cr, uid, c: self.pool.get('res.company'). _company_default_get(cr, uid, self._name, context=c), } _sql_constraints = [] ##------------------------------------------------------------------------- def button_generate_labels(self, cr, uid, ids, context=None): ids = isinstance(ids, (int, long)) and [ids] or ids buf = cStringIO.StringIO() for item in self.browse(cr, uid, ids, context={}): output_prd = item.product_id obj_lbl = self.pool.get('tcv.label.request.print.prn.export') template = item.label_template_id p_name = output_prd.name.split('/')[0].upper() for x in ('BLOQUES', '1RA', '2DA', '(POCO MOVIMIENTO)', 'LAMINAS', 'RESINADAS', 'PULIDAS', '20MM', '.'): p_name = p_name.replace(x, '') p_name = p_name.replace(' ', ' ') product_name = p_name.strip() if p_name != 'ROSA CARIBE ' \ else 'CARIBE' if not item.label_start.isdigit() or \ not item.label_end.isdigit() or \ len(item.label_start) != 11 or \ len(item.label_end) != 11 or \ item.label_start[:-2] != item.label_end[:-2] or \ int(item.label_start) > int(item.label_end): raise osv.except_osv(_('Error!'), _('Invalid labels sequence')) label_list = range(int(item.label_start), int(item.label_end) + 1) label_list = map(lambda x: '%011d' % x, label_list) label_template = obj_lbl.load_label_template(template.template) block_ref = item.block_ref price_1 = item.price_1 tax_1 = item.tax_1 price_2 = item.price_2 label_date = item.label_date labels = obj_lbl.create_labels( label_list, label_template, { 'product_name': product_name, 'block_ref': 'Ref: %s' % block_ref, 'price_1': ('PMVP: %.2f | IVA: %.2f' % (price_1, tax_1)).replace('.', ','), 'price_2': ('A pagar Bs x m2:%.2f' % (price_2)).replace('.', ','), 'label_date': label_date, }) buf.write(labels) out = base64.encodestring(buf.getvalue()) buf.close() file_name = '%s-%s.prn' % (label_list[0], label_list[-1][-2:]) self.write(cr, uid, [item.id], { 'prn_file': out, 'name': file_name, 'loaded': True }, context=context) return True ##------------------------------------------------------------ on_change... def on_change_label(self, cr, uid, ids, label_start, label_end): res = {} res.update({'Name': None, 'prn_file': None, 'loaded': False}) return {'value': res}
class account_invoice_line(osv.osv): _inherit = 'account.invoice.line' def _amount_line_single(self, cr, uid, ids, prop, unknow_none, unknow_dict): """ Provides 4 additional function fields to be used in invoice reports. Note that the original invoice report uses price_unit, which may or may not include taxes according to product settings. These fields should unequivocally produce the price per units or per line, excluding and including taxes. Analogous to the standard method _amount_line in account/account_invoice.py """ res = {} tax_obj = self.pool.get('account.tax') cur_obj = self.pool.get('res.currency') for line in self.browse(cr, uid, ids): price = line.price_unit * (1 - (line.discount or 0.0) / 100.0) taxes = tax_obj.compute_all( cr, uid, line.invoice_line_tax_id, price, 1, product=line.product_id, address_id=line.invoice_id.address_invoice_id, partner=line.invoice_id.partner_id) res[line.id] = { 'price_unit_incl': taxes['total_included'], 'price_unit_excl': taxes['total'] } taxes = tax_obj.compute_all( cr, uid, line.invoice_line_tax_id, price, line.quantity, product=line.product_id, address_id=line.invoice_id.address_invoice_id, partner=line.invoice_id.partner_id) res[line.id]['price_line_incl'] = taxes['total_included'] res[line.id]['price_line_excl'] = taxes['total'] if line.invoice_id: cur = line.invoice_id.currency_id res[line.id]['price_unit_incl'] = cur_obj.round( cr, uid, cur, res[line.id]['price_unit_incl']) res[line.id]['price_unit_excl'] = cur_obj.round( cr, uid, cur, res[line.id]['price_unit_excl']) res[line.id]['price_line_incl'] = cur_obj.round( cr, uid, cur, res[line.id]['price_line_incl']) res[line.id]['price_line_excl'] = cur_obj.round( cr, uid, cur, res[line.id]['price_line_excl']) return res _columns = { 'price_unit_incl': fields.function(_amount_line_single, string='Unit price incl. taxes', type="float", digits_compute=dp.get_precision('Account'), multi='single', store=False), 'price_unit_excl': fields.function(_amount_line_single, string='Unit price excl. taxes', type="float", digits_compute=dp.get_precision('Account'), multi='single', store=False), 'price_line_incl': fields.function(_amount_line_single, string='Line subtotal incl. taxes', type="float", digits_compute=dp.get_precision('Account'), multi='single', store=False), 'price_line_excl': fields.function(_amount_line_single, string='Line subtotal excl. taxes', type="float", digits_compute=dp.get_precision('Account'), multi='single', store=False), }
class product_historical(osv.Model): """ product_historical """ def _get_historical_price(self, cr, uid, ids, field_name, field_value, arg, context={}): res = {} product_hist = self.pool.get('product.historic.price') for id in ids: if self.browse(cr, uid, id).list_price != self.browse(cr, uid, id).\ list_price_historical: res[id] = self.browse(cr, uid, id).list_price product_hist.create(cr, uid, { 'product_id': id, 'name': time.strftime('%Y-%m-%d %H:%M:%S'), 'price': self.browse(cr, uid, id).list_price, }, context) return res def _get_historical_cost(self, cr, uid, ids, field_name, field_value, arg, context={}): res = {} product_hist = self.pool.get('product.historic.cost') for id in ids: if self.browse(cr, uid, id).standard_price != self.browse(cr, uid, id).cost_historical: res[id] = self.browse(cr, uid, id).standard_price product_hist.create(cr, uid, { 'product_id': id, 'name': time.strftime('%Y-%m-%d %H:%M:%S'), 'price': self.browse(cr, uid, id).standard_price, }, context) return res _inherit = 'product.product' _columns = { 'list_price_historical': fields.function(_get_historical_price, method=True, string='Latest Price', type='float', digits_compute=dp.get_precision( 'List_Price_Historical'), store={'product.product': ( lambda self, cr, uid, ids, c={}: ids, [ 'list_price'], 50), }, help="""Latest Recorded Historical Value"""), 'cost_historical': fields.function(_get_historical_cost, method=True, string=' Latest Cost', type='float', digits_compute=dp.get_precision( 'Cost_Historical'), store={'product.product': ( lambda self, cr, uid, ids, c={}: ids, [ 'standard_price'], 50), }, help="""Latest Recorded Historical Cost"""), 'list_price_historical_ids': fields.one2many('product.historic.price', 'product_id', 'Historical Prices'), 'cost_historical_ids': fields.one2many('product.historic.cost', 'product_id', 'Historical Prices'), }
def _link_invoice(self, cursor, uid, trans, move_lines, partner_ids, bank_account_ids, log, linked_invoices): ''' Find the invoice belonging to this reference - if there is one Use the sales journal to check. Challenges we're facing: 1. The sending or receiving party is not necessarily the same as the partner the payment relates to. 2. References can be messed up during manual encoding and inexact matching can link the wrong invoices. 3. Amounts can or can not match the expected amount. 4. Multiple invoices can be paid in one transaction. .. There are countless more, but these we'll try to address. Assumptions for matching: 1. There are no payments for invoices not sent. These are dealt with later on. 2. Debit amounts are either customer invoices or credited supplier invoices. 3. Credit amounts are either supplier invoices or credited customer invoices. 4. Payments are either below expected amount or only slightly above (abs). 5. Payments from partners that are matched, pay their own invoices. Worst case scenario: 1. No match was made. No harm done. Proceed with manual matching as usual. 2. The wrong match was made. Statements are encoded in draft. You will have the opportunity to manually correct the wrong assumptions. Return values: move_info: the move_line information belonging to the matched invoice new_trans: the new transaction when the current one was split. This can happen when multiple invoices were paid with a single bank transaction. ''' def eyecatcher(invoice): ''' Return the eyecatcher for an invoice ''' return invoice.type.startswith('in_') and invoice.name or \ invoice.number def has_id_match(invoice, ref, msg): ''' Aid for debugging - way more comprehensible than complex comprehension filters ;-) Match on ID of invoice (reference, name or number, whatever available and sensible) ''' if invoice.reference: # Reference always comes first, as it is manually set for a # reason. iref = invoice.reference.upper() if iref in ref or iref in msg: return True if invoice.type.startswith('in_'): # Internal numbering, no likely match on number if invoice.name: iname = invoice.name.upper() if iname in ref or iname in msg: return True elif invoice.type.startswith('out_'): # External id's possible and likely inum = invoice.number.upper() if inum in ref or inum in msg: return True return False def _cached(move_line): '''Check if the move_line has been cached''' return move_line.id in linked_invoices def _cache(move_line, remaining=0.0): '''Cache the move_line''' linked_invoices[move_line.id] = remaining def _remaining(move_line): '''Return the remaining amount for a previously matched move_line ''' return linked_invoices[move_line.id] def _sign(invoice): '''Return the direction of an invoice''' return {'in_invoice': -1, 'in_refund': 1, 'out_invoice': 1, 'out_refund': -1 }[invoice.type] digits = dp.get_precision('Account')(cursor)[1] partial = False # Search invoice on partner if partner_ids: candidates = [x for x in move_lines if x.partner_id.id in partner_ids and str2date(x.date, '%Y-%m-%d') <= (trans.execution_date + payment_window) and (not _cached(x) or _remaining(x)) ] else: candidates = [] # Next on reference/invoice number. Mind that this uses the invoice # itself, as the move_line references have been fiddled with on invoice # creation. This also enables us to search for the invoice number in the # reference instead of the other way around, as most human interventions # *add* text. if len(candidates) > 1 or not candidates: ref = trans.reference.upper() msg = trans.message.upper() # The manual usage of the sales journal creates moves that # are not tied to invoices. Thanks to Stefan Rijnhart for # reporting this. candidates = [x for x in candidates or move_lines if x.invoice and has_id_match(x.invoice, ref, msg) and str2date(x.invoice.date_invoice, '%Y-%m-%d') <= (trans.execution_date + payment_window) and (not _cached(x) or _remaining(x)) ] # Match on amount expected. Limit this kind of search to known # partners. if not candidates and partner_ids: candidates = [x for x in move_lines if round(abs(x.credit or x.debit), digits) == round(abs(trans.transferred_amount), digits) and str2date(x.date, '%Y-%m-%d') <= (trans.execution_date + payment_window) and (not _cached(x) or _remaining(x)) ] move_line = False if candidates and len(candidates) > 0: # Now a possible selection of invoices has been found, check the # amounts expected and received. # # TODO: currency coercing best = [x for x in candidates if round(abs(x.credit or x.debit), digits) == round(abs(trans.transferred_amount), digits) and str2date(x.date, '%Y-%m-%d') <= (trans.execution_date + payment_window) ] if len(best) == 1: # Exact match move_line = best[0] invoice = move_line.invoice if _cached(move_line): partial = True expected = _remaining(move_line) else: _cache(move_line) elif len(candidates) > 1: # Before giving up, check cache for catching duplicate # transfers first paid = [x for x in move_lines if x.invoice and has_id_match(x.invoice, ref, msg) and str2date(x.invoice.date_invoice, '%Y-%m-%d') <= trans.execution_date and (_cached(x) and not _remaining(x)) ] if paid: log.append( _('Unable to link transaction id %(trans)s ' '(ref: %(ref)s) to invoice: ' 'invoice %(invoice)s was already paid') % { 'trans': '%s.%s' % (trans.statement_id, trans.id), 'ref': trans.reference, 'invoice': eyecatcher(paid[0].invoice) }) else: # Multiple matches log.append( _('Unable to link transaction id %(trans)s (ref: %(ref)s) to invoice: ' '%(no_candidates)s candidates found; can\'t choose.') % { 'trans': '%s.%s' % (trans.statement_id, trans.id), 'ref': trans.reference, 'no_candidates': len(best) or len(candidates) }) log.append(' ' + _('Candidates: %(candidates)s') % { 'candidates': ', '.join([x.invoice.number for x in best or candidates ]) }) move_line = False partial = False elif len(candidates) == 1: # Mismatch in amounts move_line = candidates[0] invoice = move_line.invoice expected = round(_sign(invoice) * invoice.residual, digits) partial = True trans2 = None if move_line and partial: found = round(trans.transferred_amount, digits) if abs(expected) == abs(found): partial = False # Last partial payment will not flag invoice paid without # manual assistence invoice_obj = self.pool.get('account.invoice') invoice_obj.write(cursor, uid, [invoice.id], { 'state': 'paid' }) elif abs(expected) > abs(found): # Partial payment, reuse invoice _cache(move_line, expected - found) elif abs(expected) < abs(found): # Possible combined payments, need to split transaction to # verify _cache(move_line) trans2 = trans.copy() trans2.transferred_amount -= expected trans.transferred_amount = expected trans.id += 'a' trans2.id += 'b' # NOTE: the following is debatable. By copying the # eyecatcher of the invoice itself, we enhance the # tracability of the invoices, but we degrade the # tracability of the bank transactions. When debugging, it # is wise to disable this line. trans.reference = eyecatcher(move_line.invoice) if move_line: account_ids = [ x.id for x in bank_account_ids if x.partner_id.id == move_line.partner_id.id ] return ( self._get_move_info(cursor, uid, move_line, account_ids and account_ids[0] or False, partial=(partial and not trans2) ), trans2 ) return (False, False)
class account_analytic_account(orm.Model): _inherit = 'account.analytic.account' def _wip_report_fy(self, cr, uid, ids, fields, arg, context=None): res = self._wip_report(cr, uid, ids, fields, arg, context) if context is None: context = {} for account in self.browse(cr, uid, ids, context=context): all_ids = self.get_child_accounts(cr, uid, [account.id], context=context).keys() res[account.id].update({ 'fy_billings': 0, 'fy_costs': 0, 'fy_gross_profit': 0, 'fy_actual_costs': 0, 'fy_actual_material_cost': 0, 'fy_actual_labor_cost': 0, }) query_params = [tuple(all_ids)] where_date = '' if context.get('from_date_fy', False): fromdate = context.get('from_date_fy') else: raise orm.except_orm( _('Error'), _('The start date for the fiscal year has' ' not been provided.')) if context.get('from_date_fy', False): todate = context.get('to_date_fy') else: raise orm.except_orm( _('Error'), _('The end date form the fiscal year has' ' not been provided.')) where_date += " AND l.date >= %s" query_params += [fromdate] where_date += " AND l.date <= %s" query_params += [todate] # Actual billings for the fiscal year cr.execute( """SELECT amount, L.id FROM account_analytic_line L INNER JOIN account_account AC ON L.general_account_id = AC.id INNER JOIN account_account_type AT ON AT.id = AC.user_type WHERE AT.report_type = 'income' AND L.account_id IN %s """ + where_date + """ """, query_params) res[account.id]['fy_billings_line_ids'] = [] for (val, line_id) in cr.fetchall(): res[account.id]['fy_billings'] += val res[account.id]['fy_billings_line_ids'].append(line_id) # Actual costs for the fiscal year cr.execute( """SELECT COALESCE(-1*sum(amount),0.0) FROM account_analytic_line L INNER JOIN account_account AC ON L.general_account_id = AC.id INNER JOIN account_account_type AT ON AT.id = AC.user_type WHERE AT.report_type = 'expense' AND L.account_id IN %s """ + where_date + """ """, query_params) val = cr.fetchone()[0] or 0 res[account.id]['fy_costs'] = val # Revenue (add the under over) res[account.id]['fy_revenue'] = res[account.id][ 'fy_billings'] + res[account.id]['under_billings'] - res[ account.id]['over_billings'] # Gross margin res[account.id]['fy_gross_profit'] = \ res[account.id]['fy_revenue'] - res[account.id]['fy_costs'] # Actual costs to date cr.execute( """ SELECT amount, L.id, AAJ.cost_type FROM account_analytic_line L INNER JOIN account_analytic_journal AAJ ON AAJ.id = L.journal_id INNER JOIN account_account AC ON L.general_account_id = AC.id INNER JOIN account_account_type AT ON AT.id = AC.user_type WHERE AT.report_type = 'expense' AND L.account_id in %s """ + where_date + """ """, query_params) res[account.id]['fy_actual_costs'] = 0 res[account.id]['fy_actual_cost_line_ids'] = [] res[account.id]['fy_actual_material_line_ids'] = [] res[account.id]['fy_actual_labor_line_ids'] = [] for (total, line_id, cost_type) in cr.fetchall(): if cost_type in ('material', 'revenue'): res[account.id]['fy_actual_material_cost'] -= total res[account.id]['fy_actual_material_line_ids'].append( line_id) elif cost_type == 'labor': res[account.id]['fy_actual_labor_cost'] -= total res[account.id]['fy_actual_labor_line_ids'].append(line_id) res[account.id]['fy_actual_costs'] -= total res[account.id]['fy_actual_cost_line_ids'].append(line_id) return res _columns = { 'fy_revenue': fields.function( _wip_report_fy, method=True, type='float', string='Fiscal Year Revenue', multi='wip_report_fy', help="""Revenue for the provided Fiscal Year. This calculated by adding the billings for the fiscal year and the under/over billed for the contract. Thus, it will include the billings in excess of cost (under billed) and the costs in excess of billings (over billed).""", digits_compute=dp.get_precision('Account')), 'fy_billings': fields.function(_wip_report_fy, method=True, type='float', string='Fiscal Year Billings', multi='wip_report_fy', help="Billings for the provided Fiscal Year", digits_compute=dp.get_precision('Account')), 'fy_costs': fields.function(_wip_report_fy, method=True, type='float', string='Fiscal Year Costs', multi='wip_report_fy', help="Costs for the provided Fiscal Year", digits_compute=dp.get_precision('Account')), 'fy_gross_profit': fields.function(_wip_report_fy, method=True, type='float', string='Fiscal Year Gross Profit', multi='wip_report_fy', digits_compute=dp.get_precision('Account')), 'fy_actual_costs': fields.function(_wip_report_fy, method=True, type='float', string='Fiscal Year Actual Costs', multi='wip_report_fy', digits_compute=dp.get_precision('Account')), 'fy_actual_material_cost': fields.function(_wip_report_fy, method=True, type='float', string='Fiscal Year Material Costs', multi='wip_report_fy', digits_compute=dp.get_precision('Account')), 'fy_actual_labor_cost': fields.function(_wip_report_fy, method=True, type='float', string='Fiscal Year Labor Costs', multi='wip_report_fy', digits_compute=dp.get_precision('Account')), 'fy_actual_cost_line_ids': fields.function(_wip_report_fy, method=True, type='many2many', relation="account.analytic.line", string="Detail", multi='wip_report'), 'fy_actual_labor_line_ids': fields.function(_wip_report_fy, method=True, type='many2many', relation="account.analytic.line", string="Detail", multi='wip_report'), 'fy_actual_material_line_ids': fields.function(_wip_report_fy, method=True, type='many2many', relation="account.analytic.line", string="Detail", multi='wip_report'), 'fy_billings_line_ids': fields.function(_wip_report_fy, method=True, type='many2many', relation="account.analytic.line", string="Detail", multi='wip_report'), } def action_open_fy_cost_lines(self, cr, uid, ids, context=None): """ :return dict: dictionary value for created view """ if context is None: context = {} line = self.browse(cr, uid, ids[0], context) bill_lines = [x.id for x in line.fy_actual_cost_line_ids] res = self.pool.get('ir.actions.act_window').for_xml_id( cr, uid, 'account', 'action_account_tree1', context) res['domain'] = "[('id', 'in', [" + ','.join(map(str, bill_lines)) + "])]" return res def action_open_fy_material_lines(self, cr, uid, ids, context=None): """ :return dict: dictionary value for created view """ if context is None: context = {} line = self.browse(cr, uid, ids[0], context) bill_lines = [x.id for x in line.fy_actual_material_line_ids] res = self.pool.get('ir.actions.act_window').for_xml_id( cr, uid, 'account', 'action_account_tree1', context) res['domain'] = "[('id', 'in', [" + ','.join(map(str, bill_lines)) + "])]" return res def action_open_fy_labor_lines(self, cr, uid, ids, context=None): """ :return dict: dictionary value for created view """ if context is None: context = {} line = self.browse(cr, uid, ids[0], context) bill_lines = [x.id for x in line.fy_actual_labor_line_ids] res = self.pool.get('ir.actions.act_window').for_xml_id( cr, uid, 'account', 'action_account_tree1', context) res['domain'] = "[('id', 'in', [" + ','.join(map(str, bill_lines)) + "])]" return res def action_open_fy_billings_lines(self, cr, uid, ids, context=None): """ :return dict: dictionary value for created view """ if context is None: context = {} line = self.browse(cr, uid, ids[0], context) bill_lines = [x.id for x in line.fy_billings_line_ids] res = self.pool.get('ir.actions.act_window').for_xml_id( cr, uid, 'account', 'action_account_tree1', context) res['domain'] = "[('id', 'in', [" + ','.join(map(str, bill_lines)) + "])]" return res
class tcv_bank_deposit_line(osv.osv): _name = 'tcv.bank.deposit.line' _description = '' _rec_name = 'move_line' _columns = { 'line_id': fields.many2one('tcv.bank.deposit', 'Deposit lines', required=True, ondelete='cascade'), 'origin': fields.many2one('tcv.bank.config.detail', 'Origin', required=True, ondelete='restrict'), 'rel_journal': fields.related('origin', 'journal_id', type='many2one', relation='account.journal', string='Journal name', store=False, readonly=True), 'rel_forced': fields.related('origin', 'force_detail', type='boolean', string='Forced', store=False, readonly=True), 'rel_comission': fields.related('origin', 'bank_comission', type='float', string='Comission', store=False, readonly=True), 'rel_prepaid_tax': fields.related('origin', 'prepaid_tax', type='float', string='Comission', store=False, readonly=True), 'move_line': fields.many2one('account.move.line', 'Move', ondelete='restrict', domain="[('journal_id', '=', rel_journal), " + "('debit','>', 0), ('reconcile_id', '=', 0)]"), 'partner_id': fields.related('move_line', 'partner_id', type='many2one', relation='res.partner', string='Partner', store=False, readonly=True), 'amount_move': fields.related('move_line', 'debit', type='float', string='Move amount', store=False), 'amount': fields.float('Amount', digits_compute=dp.get_precision('Account')), 'dep_date': fields.related('line_id', 'date', string='Deposit date', readonly=True, type='date'), } _defaults = {} def name_get(self, cr, uid, ids, context=None): if not ids: return [] so_brw = self.browse(cr, uid, ids, context={}) res = [] for record in so_brw: name = '%s: %s - %s' % (record.origin.name, record.move_line.ref, record.partner_id.name) res.append((record.id, name)) return res def on_change_move_line(self, cr, uid, ids, move_line): res = {'value': {'amount_view': 0.0}} if move_line: move = self.pool.get('account.move.line').browse(cr, uid, move_line, context=None) res = { 'value': { 'amount_move': move.debit, 'amount': move.debit, 'partner_id': move.partner_id.id } } return res def on_change_origin(self, cr, uid, ids, origin): res = {} if origin: org = self.pool.get('tcv.bank.config.detail').browse(cr, uid, origin, context=None) res = { 'value': { 'rel_journal': org.journal_id.id, 'rel_forced': org.force_detail, 'rel_comission': org.bank_comission, 'rel_prepaid_tax': org.prepaid_tax } } return res def create(self, cr, uid, vals, context=None): if 'amount' not in vals and 'amount_move' in vals: vals.update({'amount': vals['amount_move']}) return super(tcv_bank_deposit_line, self).create(cr, uid, vals, context)
def _link_costs(self, cursor, uid, trans, period_id, account_info, log): ''' Get or create a costs invoice for the bank and return it with the payment as seen in the transaction (when not already done). ''' if not account_info.costs_account_id: return [] digits = dp.get_precision('Account')(cursor)[1] amount = round(abs(trans.transferred_amount), digits) # Make sure to be able to pinpoint our costs invoice for later # matching reference = '%s.%s: %s' % (trans.statement_id, trans.id, trans.reference) # search supplier invoice invoice_obj = self.pool.get('account.invoice') invoice_ids = invoice_obj.search(cursor, uid, [ '&', ('type', '=', 'in_invoice'), ('partner_id', '=', account_info.bank_partner_id.id), ('company_id', '=', account_info.company_id.id), ('date_invoice', '=', date2str(trans.effective_date)), ('reference', '=', reference), ('amount_total', '=', amount), ] ) if invoice_ids and len(invoice_ids) == 1: invoice = invoice_obj.browse(cursor, uid, invoice_ids)[0] elif not invoice_ids: # create supplier invoice partner_obj = self.pool.get('res.partner') invoice_lines = [(0,0,dict( amount = 1, price_unit = amount, name = trans.message or trans.reference, account_id = account_info.costs_account_id.id ))] invoice_address_id = partner_obj.address_get( cursor, uid, [account_info.bank_partner_id.id], ['invoice'] ) invoice_id = invoice_obj.create(cursor, uid, dict( type = 'in_invoice', company_id = account_info.company_id.id, partner_id = account_info.bank_partner_id.id, address_invoice_id = invoice_address_id['invoice'], period_id = period_id, journal_id = account_info.invoice_journal_id.id, account_id = account_info.bank_partner_id.property_account_payable.id, date_invoice = date2str(trans.effective_date), reference_type = 'none', reference = reference, name = trans.reference or trans.message, check_total = amount, invoice_line = invoice_lines, )) invoice = invoice_obj.browse(cursor, uid, invoice_id) # Create workflow invoice_obj.button_compute(cursor, uid, [invoice_id], {'type': 'in_invoice'}, set_total=True) wf_service = netsvc.LocalService('workflow') # Move to state 'open' wf_service.trg_validate(uid, 'account.invoice', invoice.id, 'invoice_open', cursor) # return move_lines to mix with the rest return [x for x in invoice.move_id.line_id if x.account_id.reconcile]
class sale_advance_payment_inv(osv.osv_memory): _name = "sale.advance.payment.inv" _description = "Sales Advance Payment Invoice" _columns = { 'advance_payment_method':fields.selection( [('all', 'Invoice all the Sale Order'), ('percentage','Percentage'), ('fixed','Fixed Price'), ('lines', 'Some Order Lines')], 'Type', required=True, help="""Use All to create the final invoice. Use Percentage to invoice a percentage of the total amount. Use Fixed Price to invoice a specific amound in advance. Use Some Order Lines to invoice a selection of the sale order lines."""), 'qtty': fields.float('Quantity', digits=(16, 2), required=True), 'product_id': fields.many2one('product.product', 'Advance Product', help="""Select a product of type service which is called 'Advance Product'. You may have to create it and set it as a default value on this field."""), 'amount': fields.float('Advance Amount', digits_compute= dp.get_precision('Sale Price'), help="The amount to be invoiced in advance."), } def _get_advance_product(self, cr, uid, context=None): try: product = self.pool.get('ir.model.data').get_object(cr, uid, 'sale', 'advance_product_0') except ValueError: # a ValueError is returned if the xml id given is not found in the table ir_model_data return False return product.id _defaults = { 'advance_payment_method': 'all', 'qtty': 1.0, 'product_id': _get_advance_product, } def onchange_method(self, cr, uid, ids, advance_payment_method, product_id, context=None): if advance_payment_method == 'percentage': return {'value': {'amount':0, 'product_id':False }} if product_id: product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) return {'value': {'amount': product.list_price}} return {'value': {'amount': 0}} def create_invoices(self, cr, uid, ids, context=None): """ create invoices for the active sale orders """ if context is None: context = {} wizard = self.browse(cr, uid, ids[0], context) sale_ids = context.get('active_ids', []) if wizard.advance_payment_method == 'all': # create the final invoices of the active sale orders res = self.pool.get('sale.order').manual_invoice(cr, uid, sale_ids, context) if context.get('open_invoices', False): return res return {'type': 'ir.actions.act_window_close'} if wizard.advance_payment_method == 'lines': # open the list view of sale order lines to invoice act_window = self.pool.get('ir.actions.act_window') res = act_window.for_xml_id(cr, uid, 'sale', 'action_order_line_tree2', context) res['context'] = { 'search_default_uninvoiced': 1, 'search_default_order_id': sale_ids and sale_ids[0] or False, } return res assert wizard.advance_payment_method in ('fixed', 'percentage') sale_obj = self.pool.get('sale.order') inv_obj = self.pool.get('account.invoice') inv_line_obj = self.pool.get('account.invoice.line') inv_ids = [] for sale in sale_obj.browse(cr, uid, sale_ids, context=context): if sale.order_policy == 'postpaid': raise osv.except_osv( _('Error'), _("You cannot make an advance on a sales order \ that is defined as 'Automatic Invoice after delivery'.")) val = inv_line_obj.product_id_change(cr, uid, [], wizard.product_id.id, uom=False, partner_id=sale.partner_id.id, fposition_id=sale.fiscal_position.id) res = val['value'] # determine and check income account if not wizard.product_id.id : prop = self.pool.get('ir.property').get(cr, uid, 'property_account_income_categ', 'product.category', context=context) prop_id = prop and prop.id or False account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, sale.fiscal_position.id or False, prop_id) if not account_id: raise osv.except_osv(_('Configuration Error !'), _('There is no income account defined as global property.')) res['account_id'] = account_id if not res.get('account_id'): raise osv.except_osv(_('Configuration Error !'), _('There is no income account defined for this product: "%s" (id:%d)') % \ (wizard.product_id.name, wizard.product_id.id,)) # determine invoice amount if wizard.amount <= 0.00: raise osv.except_osv(_('Incorrect Data'), _('The value of Advance Amount must be positive.')) if wizard.advance_payment_method == 'percentage': inv_amount = sale.amount_total * wizard.amount / 100 if not res.get('name'): res['name'] = _("Advance of %s %%") % (wizard.amount) else: inv_amount = wizard.amount if not res.get('name'): #TODO: should find a way to call formatLang() from rml_parse symbol = sale.pricelist_id.currency_id.symbol if sale.pricelist_id.currency_id.position == 'after': res['name'] = _("Advance of %s %s") % (inv_amount, symbol) else: res['name'] = _("Advance of %s %s") % (symbol, inv_amount) # determine taxes if res.get('invoice_line_tax_id'): res['invoice_line_tax_id'] = [(6, 0, res.get('invoice_line_tax_id'))] else: res['invoice_line_tax_id'] = False # create the invoice inv_line_values = { 'name': res.get('name'), 'account_id': res['account_id'], 'price_unit': inv_amount, 'quantity': wizard.qtty or 1.0, 'discount': False, 'uos_id': res.get('uos_id', False), 'product_id': wizard.product_id.id, 'invoice_line_tax_id': res.get('invoice_line_tax_id'), 'account_analytic_id': sale.project_id.id or False, } inv_values = { 'name': sale.client_order_ref or sale.name, 'origin': sale.name, 'type': 'out_invoice', 'reference': False, 'account_id': sale.partner_id.property_account_receivable.id, 'partner_id': sale.partner_id.id, 'invoice_line': [(0, 0, inv_line_values)], 'currency_id': sale.pricelist_id.currency_id.id, 'comment': '', 'payment_term': sale.payment_term.id, 'fiscal_position': sale.fiscal_position.id or sale.partner_id.property_account_position.id } inv_id = inv_obj.create(cr, uid, inv_values, context=context) inv_obj.button_reset_taxes(cr, uid, [inv_id], context=context) inv_ids.append(inv_id) # add the invoice to the sale order's invoices sale.write({'invoice_ids': [(4, inv_id)]}) # If invoice on picking: add the cost on the SO # If not, the advance will be deduced when generating the final invoice if sale.order_policy == 'picking': vals = { 'order_id': sale.id, 'name': res.get('name'), 'price_unit': -inv_amount, 'product_uom_qty': wizard.qtty or 1.0, 'product_uos_qty': wizard.qtty or 1.0, 'product_uos': res.get('uos_id', False), 'product_uom': res.get('uom_id', False), 'product_id': wizard.product_id.id or False, 'discount': False, 'tax_id': res.get('invoice_line_tax_id'), } self.pool.get('sale.order.line').create(cr, uid, vals, context=context) if context.get('open_invoices', False): return self.open_invoices( cr, uid, ids, inv_ids, context=context) return {'type': 'ir.actions.act_window_close'} def open_invoices(self, cr, uid, ids, invoice_ids, context=None): """ open a view on one of the given invoice_ids """ ir_model_data = self.pool.get('ir.model.data') form_res = ir_model_data.get_object_reference(cr, uid, 'account', 'invoice_form') form_id = form_res and form_res[1] or False tree_res = ir_model_data.get_object_reference(cr, uid, 'account', 'invoice_tree') tree_id = tree_res and tree_res[1] or False return { 'name': _('Advance Invoice'), 'view_type': 'form', 'view_mode': 'form,tree', 'res_model': 'account.invoice', 'res_id': invoice_ids[0], 'view_id': False, 'views': [(form_id, 'form'), (tree_id, 'tree')], 'context': "{'type': 'out_invoice'}", 'type': 'ir.actions.act_window', }
class tcv_bank_deposit(osv.osv): _name = 'tcv.bank.deposit' _description = 'Modulo de gestion de planillas de depositos bancarios' _order = 'date,ref desc' STATE_SELECTION = [('draft', 'Draft'), ('posted', 'Posted'), ('cancel', 'Cancelled')] def copy(self, cr, uid, id, default={}, context=None, done_list=[], local=False): item = self.browse(cr, uid, id, context=context) default = default or {} default = default.copy() default.update({ 'name': (item.name or '') + '(copy)', 'ref': '/', 'narration': '', 'state': 'draft', 'move_id': False, }) return super(tcv_bank_deposit, self).copy(cr, uid, id, default, context=context) def _compute_comission(self, cr, uid, line, comission): total = (line.amount * comission) / 100 res = round( total, self.pool.get('decimal.precision').precision_get( cr, uid, 'Account')) return res def _total_deposit_all(self, cr, uid, ids, field_name, arg, context=None): res = {} for dep in self.browse(cr, uid, ids, context=context): res[dep.id] = { 'cash_total': 0.0, 'cheq_total': 0.0, 'debit_total': 0.0, 'comission_total': 0.0, 'prepaid_total': 0.0, 'amount_total': 0.0, } for line in dep.line_ids: if line.origin.type == 'cash': res[dep.id]['cash_total'] += line.amount elif line.origin.type == 'cheq': res[dep.id]['cheq_total'] += line.amount elif line.origin.type == 'debit': res[dep.id]['debit_total'] += line.amount res[dep.id]['comission_total'] += self._compute_comission( cr, uid, line, line.rel_comission) res[dep.id]['prepaid_total'] += self._compute_comission( cr, uid, line, line.rel_prepaid_tax) if res[dep.id]['comission_total']: res[dep.id]['comission_total'] += dep.comission_dif res[dep.id]['amount_total'] = res[dep.id]['cash_total'] + \ res[dep.id]['cheq_total'] + res[dep.id]['debit_total'] - \ res[dep.id]['comission_total'] - res[dep.id]['prepaid_total'] return res def _get_account_balance(self, cr, uid, account_id, operator, date_balance, context): cr.execute(''' select sum(debit-credit) as balance_in_currency FROM account_move_line l left join account_move m on l.move_id = m.id WHERE l.account_id = %s AND l.date %s '%s' AND m.state = 'posted' ''' % (account_id, operator, date_balance)) return cr.dictfetchone()['balance_in_currency'] or 0 _columns = { 'ref': fields.char('Reference', size=32, readonly=True), 'name': fields.char('Document number', size=32, readonly=True, states={'draft': [('readonly', False)]}), 'bank_journal_id': fields.many2one('account.journal', 'Bank journal', required=True, readonly=True, states={'draft': [('readonly', False)]}, ondelete='restrict'), 'date': fields.date('Date', required=True, readonly=True, states={'draft': [('readonly', False)]}, select=True), 'company_id': fields.many2one('res.company', 'Company', required=True, readonly=True, ondelete='restrict'), 'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, ondelete='restrict', states={'draft': [('readonly', False)]}), 'move_id': fields.many2one('account.move', 'Account move', ondelete='restrict', help="The move of this entry line.", select=2, readonly=True), 'state': fields.selection(STATE_SELECTION, string='State', required=True, readonly=True), 'check_total': fields.float('Total', digits_compute=dp.get_precision('Account'), readonly=True, states={'draft': [('readonly', False)]}), 'comission_dif': fields.float('Comission dif. (±1)', digits_compute=dp.get_precision('Account'), readonly=True, states={'draft': [('readonly', False)]}, help="You can set here a small diference in calculated " + "comission ammount."), 'line_ids': fields.one2many('tcv.bank.deposit.line', 'line_id', 'Details', readonly=True, states={'draft': [('readonly', False)]}, ondelete='cascade'), 'cash_total': fields.function(_total_deposit_all, method=True, digits_compute=dp.get_precision('Account'), string='Cash total (+)', store=False, multi='all'), 'cheq_total': fields.function(_total_deposit_all, method=True, digits_compute=dp.get_precision('Account'), string='Cheq total (+)', store=False, multi='all'), 'debit_total': fields.function(_total_deposit_all, method=True, digits_compute=dp.get_precision('Account'), string='Debit/credit total (+)', store=False, multi='all'), 'comission_total': fields.function(_total_deposit_all, method=True, digits_compute=dp.get_precision('Account'), string='Comission total (-)', store=False, multi='all'), 'prepaid_total': fields.function(_total_deposit_all, method=True, digits_compute=dp.get_precision('Account'), string='Prepaid tax total (-)', store=False, multi='all'), 'amount_total': fields.function(_total_deposit_all, method=True, digits_compute=dp.get_precision('Account'), string='General total (=)', store=False, multi='all'), 'narration': fields.text('Notes', readonly=False), } _defaults = { 'ref': lambda *a: '/', 'name': lambda *a: '', 'check_total': lambda *a: 0.0, 'date': lambda *a: time.strftime('%Y-%m-%d'), 'company_id': lambda self, cr, uid, c: self.pool.get('res.company'). _company_default_get(cr, uid, self._name, context=c), 'currency_id': lambda self, cr, uid, c: self.pool.get('res.users').browse( cr, uid, uid, c).company_id.currency_id.id, 'state': 'draft', } _sql_constraints = [ ('deposit_name_uniq', 'UNIQUE(name, bank_journal_id)', 'The reference must be unique for this bank journal!'), ('max_comission_dif', 'CHECK(comission_dif between -1 and 1)', 'The maximum difference comsion must be in -1 to 1 range'), ] def compute_dif(self, cr, uid, ids, context=None): ids = isinstance(ids, (int, long)) and [ids] or ids for item in self.browse(cr, uid, ids, context={}): if item.check_total != item.amount_total: dif = item.amount_total - item.check_total + item.comission_dif if abs(dif) > 1: raise osv.except_osv(_('Error!'), _(u'Computed dif > ±1')) self.write(cr, uid, [item.id], {'comission_dif': dif}, context=context) return True def button_draft(self, cr, uid, ids, context=None): obj = self.pool.get('tcv.bank.deposit') vals = {'state': 'draft'} return obj.write(cr, uid, ids, vals, context) def button_posted(self, cr, uid, ids, context=None): context = context or {} if len(ids) != 1: raise osv.except_osv(_('Error!'), _('Multiplies validations not allowed.')) for item in self.browse(cr, uid, ids, context={}): if not item.ref or item.ref == '/': ref = self.pool.get('ir.sequence').get(cr, uid, 'bank.deposit') else: ref = item.ref context.update({'deposit_reference': ref}) obj = self.pool.get('tcv.bank.deposit') vals = { 'state': 'posted', 'ref': ref, 'move_id': self._gen_account_move(cr, uid, ids, context) } return obj.write(cr, uid, ids, vals, context) def button_cancel(self, cr, uid, ids, context=None): ids = isinstance(ids, (int, long)) and [ids] or ids res = {} for dep in self.browse(cr, uid, ids, context={}): if dep.state == 'posted': if dep.move_id.id: obj_move = self.pool.get('account.move') move = obj_move.browse(cr, uid, dep.move_id.id, context=None) if move.state == 'draft': recon_id = 0 for line in dep.line_ids: if line.move_line and not recon_id and \ line.move_line.reconcile_id.id: recon_id = line.move_line.reconcile_id.id if recon_id: obj_lines = self.pool.get('account.move.line') rec_ids = obj_lines.search( cr, uid, [('reconcile_id', '=', recon_id)]) obj_lines._remove_move_reconcile(cr, uid, rec_ids, context=None) obj = self.pool.get('tcv.bank.deposit') vals = {'state': 'cancel', 'move_id': 0} res = obj.write(cr, uid, ids, vals, context) obj_move.unlink(cr, uid, [move.id]) elif dep.state == 'draft': obj = self.pool.get('tcv.bank.deposit') vals = {'state': 'cancel', 'move_id': 0} res = obj.write(cr, uid, ids, vals, context) return res def test_draft(self, cr, uid, ids, *args): return True def test_posted(self, cr, uid, ids, *args): so_brw = self.browse(cr, uid, ids, context={}) for dep in so_brw: if time.strptime(dep.date, '%Y-%m-%d') > time.localtime(): raise osv.except_osv(_('Invalid date!'), _('The date must be <= today.')) if not dep.amount_total: raise osv.except_osv(_('No valid amount!'), _('The total must be > 0')) if abs(dep.amount_total - dep.check_total) > 0.0001: raise osv.except_osv( _('Bad total !'), _('Please verify the lines of the document ! The real ' + 'total does not match the computed total.')) if not dep.name: raise osv.except_osv(_('Error!'), _('You must set a document number.')) #~ lines checks chk_origin = {'cash_cheq': 0} chk_cash = {} chk_cash_info = [] for line in dep.line_ids: config = self.pool.get('tcv.bank.config.detail').browse( cr, uid, line.origin.id, context=None) #~ Validar grupo de medios de pagos seleccionados if config.type in ('cash', 'cheq'): chk_origin['cash_cheq'] += 1 if config.type == 'cash': #~ Acumulate cash total in chk_cash if line.origin.id not in chk_cash: chk_cash.update({line.origin.id: 0}) chk_cash_info.append({ 'cash_acc': line.rel_journal.default_debit_account_id.id, 'code': line.origin.id, 'acc_name': line.rel_journal.default_debit_account_id.name }) chk_cash[line.origin.id] += line.amount else: # debit and credit if line.origin.id not in chk_origin: chk_origin.update({line.origin.id: 1}) else: chk_origin[line.origin.id] += 1 if not line.rel_journal.default_debit_account_id.id or \ not line.rel_journal.default_credit_account_id.id: raise osv.except_osv( _('Error!'), _('You must set a debit and credit account for ' + 'journal: %s.') % (line.rel_journal.name)) if config.type == 'debit' and \ dep.bank_journal_id.id != config.bank_journal_id.id: raise osv.except_osv( _('Error!'), _('The bank journal differs from bank journals ' + 'payment method. You must be select: %s journal') % (config.bank_journal_id.name)) if not chk_origin['cash_cheq']: chk_origin.pop('cash_cheq') if len(chk_origin) != 1: raise osv.except_osv( _('Error!'), _('You can not mix different types of payments.')) #~ Check if cash account balance < cash in deposit for cash in chk_cash_info: balance = self._get_account_balance(cr, uid, cash['cash_acc'], '<=', dep.date, context={}) if chk_cash[cash['code']] > balance: raise osv.except_osv( _('Error!'), _('You can not deposit more cash than is in the ' + 'account: %s: %.2f') % (cash['acc_name'], balance)) return True def test_cancel(self, cr, uid, ids, *args): for dep in self.browse(cr, uid, ids, context={}): if dep.move_id and dep.move_id.state == 'posted': raise osv.except_osv( _('Error!'), _('You can not cancel a deposit while the account ' + 'move is posted.')) return True def button_calculate_click(self, cr, uid, ids, context=None): return True def _gen_account_move_line(self, company_id, partner_id, account_id, name, debit, credit): return (0, 0, { 'auto': True, 'company_id': company_id, 'partner_id': partner_id, 'account_id': account_id, 'name': name, 'debit': round(debit, 2), 'credit': round(credit, 2), 'reconcile': False, }) def _gen_account_move(self, cr, uid, ids, context=None): need_reconcile = False so_brw = self.browse(cr, uid, ids, context={}) obj_move = self.pool.get('account.move') obj_per = self.pool.get('account.period') move_id = None for dep in so_brw: obj_cfg = self.pool.get('tcv.bank.config') cfg_id = obj_cfg.search( cr, uid, [('company_id', '=', dep.company_id.id)])[0] config = obj_cfg.browse(cr, uid, cfg_id, None) move = { 'ref': '%s - Nro %s' % (context.get('deposit_reference', 'dp'), dep.name), 'journal_id': dep.bank_journal_id.id, 'date': dep.date, 'period_id': obj_per.find(cr, uid, dep.date)[0], 'company_id': dep.company_id.id, 'state': 'draft', 'to_check': False, 'narration': '', } lines = [] for line in dep.line_ids: # move line for deposit lines lines.append( self._gen_account_move_line( dep.company_id.id, line.partner_id.id, line.rel_journal.default_credit_account_id.id, line.move_line.name or line.rel_journal.name, 0, line.amount)) need_reconcile = need_reconcile or line.move_line.id if dep.comission_total: # move line for comission lines.append( self._gen_account_move_line(dep.company_id.id, False, config.acc_bank_comis.id, move['ref'], dep.comission_total, 0)) if dep.prepaid_total: # move line for prepaid tax lines.append( self._gen_account_move_line(dep.company_id.id, False, config.acc_prepaid_tax.id, move['ref'], dep.prepaid_total, 0)) # move line for deposit's bank credit lines.append( self._gen_account_move_line( dep.company_id.id, False, dep.bank_journal_id.default_debit_account_id.id, move['ref'], dep.check_total, 0)) move.update({'line_id': lines}) move_id = obj_move.create(cr, uid, move, context) obj_move.post(cr, uid, [move_id], context=context) if need_reconcile and move_id: self.do_reconcile(cr, uid, dep, move_id, context) return move_id def unlink(self, cr, uid, ids, context=None): so_brw = self.browse(cr, uid, ids, context={}) unlink_ids = [] for dep in so_brw: if dep.state in ('draft', 'cancel'): unlink_ids.append(dep['id']) else: raise osv.except_osv( _('Invalid action !'), _('Cannot delete deposit(s) that are already postedd!')) super(tcv_bank_deposit, self).unlink(cr, uid, unlink_ids, context) return True def do_reconcile(self, cr, uid, dep, move_id, context): obj_mov = self.pool.get('account.move') move = obj_mov.browse(cr, uid, move_id, context=context) rec_ids = [] for line in dep.line_ids: if line.move_line.id: rec_ids.append(line.move_line.id) for move_line in move.line_id: if line.amount == move_line.credit and \ line.move_line.account_id.id == \ move_line.account_id.id: if move_line.id not in rec_ids: rec_ids.append(move_line.id) if rec_ids: obj_move_line = self.pool.get('account.move.line') obj_move_line.reconcile(cr, uid, rec_ids, context=context) return True
class sale_order(osv.osv): _inherit = 'sale.order' def onchange_partner_id(self, cr, uid, ids, part, shop_id=False, fiscal_operation_category_id=False): result = super(sale_order, self).onchange_partner_id(cr, uid, ids, part) result['value']['fiscal_position'] = False if not part or not shop_id: return { 'value': { 'partner_invoice_id': False, 'partner_shipping_id': False, 'partner_order_id': False, 'payment_term': False, 'fiscal_position': False, 'fiscal_operation_id': False } } obj_partner = self.pool.get('res.partner').browse(cr, uid, part) fiscal_position = obj_partner.property_account_position.id partner_fiscal_type = obj_partner.partner_fiscal_type_id.id if fiscal_position: result['value']['fiscal_position'] = fiscal_position result['value'][ 'fiscal_operation_id'] = obj_partner.property_account_position.fiscal_operation_id.id return result obj_shop = self.pool.get('sale.shop').browse(cr, uid, shop_id) company_addr = self.pool.get('res.partner').address_get( cr, uid, [obj_shop.company_id.partner_id.id], ['default']) company_addr_default = self.pool.get('res.partner.address').browse( cr, uid, [company_addr['default']])[0] from_country = company_addr_default.country_id.id from_state = company_addr_default.state_id.id partner_addr_default = self.pool.get('res.partner.address').browse( cr, uid, [result['value']['partner_invoice_id']])[0] to_country = partner_addr_default.country_id.id to_state = partner_addr_default.state_id.id fsc_pos_id = self.pool.get('account.fiscal.position.rule').search( cr, uid, [('company_id', '=', obj_shop.company_id.id), ('from_country', '=', from_country), ('from_state', '=', from_state), ('to_country', '=', to_country), ('to_state', '=', to_state), ('use_sale', '=', True), ('fiscal_operation_category_id', '=', fiscal_operation_category_id), ('partner_fiscal_type_id', '=', partner_fiscal_type)]) if not fsc_pos_id: fsc_pos_id = self.pool.get('account.fiscal.position.rule').search( cr, uid, [('company_id', '=', obj_shop.company_id.id), ('from_country', '=', from_country), ('from_state', '=', from_state), ('to_country', '=', to_country), ('to_state', '=', to_state), ('use_sale', '=', True), ('fiscal_operation_category_id', '=', fiscal_operation_category_id)]) if fsc_pos_id: obj_fpo_rule = self.pool.get( 'account.fiscal.position.rule').browse(cr, uid, fsc_pos_id)[0] result['value'][ 'fiscal_position'] = obj_fpo_rule.fiscal_position_id.id result['value'][ 'fiscal_operation_id'] = obj_fpo_rule.fiscal_position_id.fiscal_operation_id.id return result def onchange_partner_invoice_id(self, cr, uid, ids, ptn_invoice_id, ptn_id, shop_id, fiscal_operation_category_id): result = super(sale_order, self).onchange_partner_invoice_id( cr, uid, ids, ptn_invoice_id, ptn_id, shop_id) result['value']['fiscal_position'] = False if not shop_id or not ptn_invoice_id or not ptn_id or not fiscal_operation_category_id: return result partner = self.pool.get('res.partner').browse(cr, uid, ptn_id) fiscal_position = partner.property_account_position.id or False partner_fiscal_type = partner.partner_fiscal_type_id.id if fiscal_position: result['value']['fiscal_position'] = fiscal_position result['value'][ 'fiscal_operation_id'] = obj_partner.property_account_position.fiscal_operation_id.id return result obj_shop = self.pool.get('sale.shop').browse(cr, uid, shop_id) company_addr = self.pool.get('res.partner').address_get( cr, uid, [obj_shop.company_id.partner_id.id], ['default']) company_addr_default = self.pool.get('res.partner.address').browse( cr, uid, [company_addr['default']])[0] from_country = company_addr_default.country_id.id from_state = company_addr_default.state_id.id partner_addr_invoice = self.pool.get('res.partner.address').browse( cr, uid, [ptn_invoice_id])[0] to_country = partner_addr_invoice.country_id.id to_state = partner_addr_invoice.state_id.id fsc_pos_id = self.pool.get('account.fiscal.position.rule').search( cr, uid, [('company_id', '=', obj_shop.company_id.id), ('from_country', '=', from_country), ('from_state', '=', from_state), ('to_country', '=', to_country), ('to_state', '=', to_state), ('use_sale', '=', True), ('fiscal_operation_category_id', '=', fiscal_operation_category_id), ('partner_fiscal_type_id', '=', partner_fiscal_type)]) if not fsc_pos_id: fsc_pos_id = self.pool.get('account.fiscal.position.rule').search( cr, uid, [('company_id', '=', obj_shop.company_id.id), ('from_country', '=', from_country), ('from_state', '=', from_state), ('to_country', '=', to_country), ('to_state', '=', to_state), ('use_sale', '=', True), ('fiscal_operation_category_id', '=', fiscal_operation_category_id)]) if fsc_pos_id: obj_fpo_rule = self.pool.get( 'account.fiscal.position.rule').browse(cr, uid, fsc_pos_id)[0] result['value'][ 'fiscal_position'] = obj_fpo_rule.fiscal_position_id.id result['value'][ 'fiscal_operation_id'] = obj_fpo_rule.fiscal_position_id.fiscal_operation_id.id return result def onchange_shop_id(self, cr, uid, ids, shop_id, ptn_id=False, ptn_invoice_id=False): result = super(sale_order, self).onchange_shop_id(cr, uid, ids, shop_id, ptn_id) result['value']['fiscal_position'] = False result['value']['fiscal_operation_id'] = False if not shop_id: result['value']['fiscal_operation_category_id'] = False return result obj_shop = self.pool.get('sale.shop').browse(cr, uid, shop_id) fiscal_operation_category_id = obj_shop.default_fo_category_id.id result['value'][ 'fiscal_operation_category_id'] = fiscal_operation_category_id if not ptn_id or not ptn_invoice_id: return result obj_partner = self.pool.get('res.partner').browse(cr, uid, ptn_id) fiscal_position = obj_partner.property_account_position.id partner_fiscal_type = obj_partner.partner_fiscal_type_id.id if fiscal_position: result['value']['fiscal_position'] = fiscal_position result['value'][ 'fiscal_operation_id'] = obj_partner.property_account_position.fiscal_operation_id.id return result obj_shop = self.pool.get('sale.shop').browse(cr, uid, shop_id) company_addr = self.pool.get('res.partner').address_get( cr, uid, [obj_shop.company_id.partner_id.id], ['default']) company_addr_default = self.pool.get('res.partner.address').browse( cr, uid, [company_addr['default']])[0] from_country = company_addr_default.country_id.id from_state = company_addr_default.state_id.id partner_addr_default = self.pool.get('res.partner.address').browse( cr, uid, [ptn_invoice_id])[0] to_country = partner_addr_default.country_id.id to_state = partner_addr_default.state_id.id fsc_pos_id = self.pool.get('account.fiscal.position.rule').search( cr, uid, [('company_id', '=', obj_shop.company_id.id), ('from_country', '=', from_country), ('from_state', '=', from_state), ('to_country', '=', to_country), ('to_state', '=', to_state), ('use_sale', '=', True), ('fiscal_operation_category_id', '=', fiscal_operation_category_id), ('partner_fiscal_type_id', '=', partner_fiscal_type)]) if not fsc_pos_id: fsc_pos_id = self.pool.get('account.fiscal.position.rule').search( cr, uid, [('company_id', '=', obj_shop.company_id.id), ('from_country', '=', from_country), ('from_state', '=', from_state), ('to_country', '=', to_country), ('to_state', '=', to_state), ('use_sale', '=', True), ('fiscal_operation_category_id', '=', fiscal_operation_category_id)]) if fsc_pos_id: obj_fpo_rule = self.pool.get( 'account.fiscal.position.rule').browse(cr, uid, fsc_pos_id)[0] result['value'][ 'fiscal_position'] = obj_fpo_rule.fiscal_position_id.id result['value'][ 'fiscal_operation_id'] = obj_fpo_rule.fiscal_position_id.fiscal_operation_id.id return result def action_invoice_create(self, cr, uid, ids, grouped=False, states=['confirmed', 'done', 'exception'], date_inv=False, context=None): result = super(sale_order, self).action_invoice_create(cr, uid, ids, grouped, states, date_inv, context) if not result: return result for order in self.browse(cr, uid, ids): for order_line in order.order_line: for inv_line in order_line.invoice_lines: invoice_id = inv_line.invoice_id fiscal_operation_id = order_line.fiscal_operation_id or order.fiscal_operation_id fiscal_operation_category_id = order_line.fiscal_operation_category_id or order.fiscal_operation_category_id if invoice_id == inv_line.invoice_id: for invoice in order.invoice_ids: if invoice.state in ('draft'): company_id = self.pool.get( 'res.company').browse( cr, uid, order.company_id.id) if not company_id.document_serie_product_ids: raise osv.except_osv( _('No fiscal document serie found !'), _("No fiscal document serie found for selected company %s and fiscal operation: '%s'" ) % (order.company_id.name, order.fiscal_operation_id.code)) comment = '' if order_line.fiscal_operation_id.inv_copy_note: comment = order_line.fiscal_operation_id.note if order.note: comment += ' - ' + order.note journal_ids = [ jou for jou in order. fiscal_operation_category_id.journal_ids if jou.company_id == invoice.company_id ] if journal_ids: journal_id = journal_ids[0].id else: journal_id = invoice_id.journal_id.id self.pool.get('account.invoice').write( cr, uid, invoice_id.id, { 'fiscal_operation_category_id': fiscal_operation_category_id.id, 'fiscal_operation_id': fiscal_operation_id.id, 'cfop_id': fiscal_operation_id.cfop_id.id, 'fiscal_document_id': fiscal_operation_id.fiscal_document_id. id, 'document_serie_id': company_id. document_serie_product_ids[0].id, 'comment': comment, 'journal_id': journal_id }) invoice_id = inv_line.invoice_id self.pool.get('account.invoice.line').write( cr, uid, inv_line.id, { 'fiscal_operation_category_id': fiscal_operation_category_id.id, 'fiscal_operation_id': fiscal_operation_id.id, 'cfop_id': fiscal_operation_id.cfop_id.id }) return result def action_ship_create(self, cr, uid, ids, *args): result = super(sale_order, self).action_ship_create(cr, uid, ids, *args) for order in self.browse(cr, uid, ids, context={}): for picking in order.picking_ids: self.pool.get('stock.picking').write( cr, uid, picking.id, { 'fiscal_operation_category_id': order.fiscal_operation_category_id.id, 'fiscal_operation_id': order.fiscal_operation_id.id, 'fiscal_position': order.fiscal_position.id }) return result def _amount_line_tax(self, cr, uid, line, context=None): val = 0.0 for c in self.pool.get('account.tax').compute_all( cr, uid, line.tax_id, line.price_unit * (1 - (line.discount or 0.0) / 100.0), line.product_uom_qty, line.order_id.partner_invoice_id.id, line.product_id, line.order_id.partner_id)['taxes']: tax_brw = self.pool.get('account.tax').browse(cr, uid, c['id']) if tax_brw.tax_add: val += c.get('amount', 0.0) return val def _amount_all(self, cr, uid, ids, field_name, arg, context): res = super(sale_order, self)._amount_all(cr, uid, ids, field_name, arg, context) return res def _get_order(self, cr, uid, ids, context={}): result = super(sale_order, self)._get_order(cr, uid, ids, context) return result.keys() _columns = { 'fiscal_operation_category_id': fields.many2one( 'l10n_br_account.fiscal.operation.category', 'Categoria', domain="[('type','=','output'),('use_sale','=',True)]"), 'fiscal_operation_id': fields.many2one( 'l10n_br_account.fiscal.operation', 'Operação Fiscal', domain= "[('fiscal_operation_category_id','=',fiscal_operation_category_id),('type','=','output'),('use_sale','=',True)]" ), 'amount_untaxed': fields.function( _amount_all, method=True, digits_compute=dp.get_precision('Sale Price'), string='Untaxed Amount', store={ 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10), 'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10) }, multi='sums'), 'amount_tax': fields.function( _amount_all, method=True, digits_compute=dp.get_precision('Sale Price'), string='Taxes', store={ 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10), 'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10), }, multi='sums'), 'amount_total': fields.function( _amount_all, method=True, digits_compute=dp.get_precision('Sale Price'), string='Total', store={ 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10), 'sale.order.line': (_get_order, ['price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10), }, multi='sums'), }