def sale_lines(self, record): product_obj = self.session.pool.get('product.product') pricelist_obj = self.session.pool.get('product.pricelist') order_lines = [] for sale_line in record['line']: # TODO: Can handle UoM with sale_line['ordunit'] if they vary from single units pricelist_id = self.backend_record.shop_id.pricelist_id.id product_binder = self.get_binder_for_model('bots.product') product_id = product_binder.to_openerp(sale_line['product_sku']) if not product_id: raise NoExternalId("Product %s could not be found in OpenERP" % (sale_line['product_sku'],)) product = product_obj.browse(self.session.cr, self.session.uid, product_id) product_qty = int(sale_line['qty']) if not self.backend_record.catalog_price_tax_included: list_values = pricelist_obj.price_get(self.session.cr, self.session.uid, [pricelist_id], product_id, product_qty) list_price = list_values[pricelist_id] if list_price: # FIXME: Due to the precision of the discount we need to reverse engineer the list price in the SO # line so we get the correct total price while keeping the list price as close as possible to the origional discount_dp = dp.get_precision('Discount')(self.session.cr)[1] discount = round((1 - float(sale_line['price'])/list_price) * 100.0, discount_dp) list_price = float(sale_line['price']) / (1.0 - (discount / 100.0)) else: list_price = float(sale_line['price']) discount = 0 else: raise NotImplementedError('Only tax exclusive prices are implimented currently, disable "Prices include tax" in the backend') order_line = [0, 0, {'name': product.name, 'product_id': product_id, 'product_uom_qty': product_qty, 'price_unit': list_price, 'discount': discount, }] order_lines.append(order_line) return {'order_line': order_lines}
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 osv.except_osv(_('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 simplified_limit_check(self): for order in self: if not order.simplified: continue limit = order.session_id.config_id.simplified_invoice_limit amount_total = order.amount_total precision_digits = dp.get_precision('Account')(self.env.cr)[1] # -1 or 0: amount_total <= limit, simplified # 1: amount_total > limit, can not be simplified simplified = ( float_compare(amount_total, limit, precision_digits=precision_digits) <= 0) # Change simplified flag if incompatible if not simplified: order.write({'simplified': simplified})
def _qty_all_2(self, cr, uid, ids, field_name, arg, context=None): result = {} for line in self.browse(cr, uid, ids, context=context): qty = line.qty2_2 uom = line.uom_id.factor_inv qty_all = round(qty*uom,3) result[line.id] = qty_all return result _columns = { 'name' : fields.char('Name', required=True), 'partner_id' : fields.many2one('res.partner','Principle',domain=[('supplier','=',True)],required=True), 'product_id' : fields.many2one('product.product','Bonus Product'), 'qty_2' : fields.float('Bonus Qty2', digits_compute=dp.get_precision('Product Unit of Measure')), 'qty' : fields.function(_qty_all_1,type="float",string='Bonus Qty',digits_compute=dp.get_precision('Product Unit of Measure')), 'uom_id' : fields.many2one('product.uom','UoM',required=True), 'uom_id2' : fields.many2one('product.uom','UoM',required=True), 'value' : fields.float('Price Value',domain=[('is_percent','=',False)]), 'per_product' : fields.boolean('Per Product'), 'persentase' : fields.float('Percent Value', digits_compute= dp.get_precision('Discount'),domain=[('is_percent','=',True)]), 'multi' : fields.boolean('Multiples'), 'is_active' : fields.boolean('Active?'), 'date_from' : fields.date('Start Date', required=True), 'date_to' : fields.date('End Date', required=True), 'condition_ids' : fields.one2many('master.condition','discount_id','Value Condition'), 'condition2_ids' : fields.one2many('master.condition2','discount_id','Product Condition'), 'condition3_ids' : fields.one2many('master.condition3','discount_id','Product Condition 2'), 'condition4_ids' : fields.one2many('master.condition4','discount_id','Product Condition 3'), 'condition5_ids' : fields.one2many('master.condition5','discount_id','Product Condition 4'),
class proforma_invoice_line(osv.osv): def _amount_line(self, cr, uid, ids, prop, unknow_none, unknow_dict): 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 taxes = tax_obj.compute_all(cr, uid, line.invoice_line_tax_id, price, line.quantity, product=line.product_id, partner=line.invoice_id.partner_id) res[line.id] = taxes['total'] if line.invoice_id: cur = line.invoice_id.currency_id res[line.id] = cur_obj.round(cr, uid, cur, res[line.id]) return res _name = "proforma.invoice.line" _description = "Proforma Invoice Line" _order = "invoice_id,sequence,id" _columns = { 'name': fields.text('Description', required=True), 'origin': fields.char( 'Source Document', size=256, help= "Reference of the document that produced this proforma invoice."), 'sequence': fields.integer( 'Sequence', help="Gives the sequence of this line when displaying the invoice." ), 'invoice_id': fields.many2one('proforma.invoice', 'Proforma Invoice', ondelete='cascade', select=True), 'uom_id': fields.many2one('product.uom', 'Unit of Measure', ondelete='set null', select=True), 'product_id': fields.many2one('product.product', 'Product', ondelete='set null', select=True), 'price_unit': fields.float('Unit Price', required=True, digits_compute=dp.get_precision('Product Price')), 'price_subtotal': fields.function(_amount_line, string='Amount', type="float", digits_compute=dp.get_precision('Account')), 'invoice_line_tax_id': fields.many2many('account.tax', 'proforma_invoice_line_tax', 'proforma_invoice_line_id', 'tax_id', 'Taxes', domain=[('parent_id', '=', False)]), 'quantity': fields.float( 'Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True), 'company_id': fields.related('invoice_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True), 'partner_id': fields.related('invoice_id', 'partner_id', type='many2one', relation='res.partner', string='Partner', store=True) } _defaults = { 'quantity': 1, 'price_unit': 0.0, 'sequence': 10, }
class formulir_1111(osv.osv): _name = 'pajak.formulir_1111' _description = 'SPT Masa PPN' _inherit = ['mail.thread'] def default_state(self, cr, uid, context={}): return 'draft' def default_name(self, cr, uid, context={}): return '/' def default_company_id(self, cr, uid, context={}): #TODO : Ticket #29 obj_res_company = self.pool.get('res.company') company_id = obj_res_company._company_default_get(cr, uid, 'res.partner', context=context) return company_id def default_created_time(self, cr, uid, context={}): #TODO: Ticket #30 return datetime.now().strftime('%Y-%m-%d') def default_created_user_id(self, cr, uid, context={}): return uid _columns = { 'name': fields.char(string='# SPT', size=30, required=True, readonly=True), 'company_id': fields.many2one(obj='res.company', string='Nama PKP', required=True), 'company_npwp': fields.char(string='NPWP', size=30, required=True), 'company_address': fields.char(string='Alamat', size=255, required=True), 'masa_pajak_id': fields.many2one(string='Masa Pajak', obj='pajak.masa_pajak', required=True), 'tahun_pajak_id': fields.related('masa_pajak_id', 'tahun_pajak_id', string='Tahun Pajak', type='many2one', relation='pajak.tahun_pajak', store=True), 'telepon': fields.char(string='Telepon', size=100), 'hp': fields.char(string='HP', size=100), 'klu': fields.char(string='KLU', size=100), 'note': fields.text(string='Note'), 'pembetulan_ke': fields.integer(string='Pembetulan Ke'), 'wajib_ppnbm': fields.boolean(string='Wajib PPnBM'), # BAGIAN 1 'item1A1': fields.float(string='1. Export', digits_compute=dp.get_precision('Account')), 'item1A2_dpp': fields.float( string='2. Penyerahan yang PPN-nya harus dipungut sendiri', digits_compute=dp.get_precision('Account')), 'item1A2_ppn': fields.float( string='2. Penyerahan yang PPN-nya harus dipungut sendiri', digits_compute=dp.get_precision('Account')), 'item1A3_dpp': fields.float( string='3. Penyerahan yang PPN-nya dipungut oleh pemungut PPN', digits_compute=dp.get_precision('Account')), 'item1A3_ppn': fields.float( string='3. Penyerahan yang PPN-nya dipungut oleh pemungut PPN', digits_compute=dp.get_precision('Account')), 'item1A4_dpp': fields.float(string='4. Penyerahan yang PPN-nya tidak dipungut', digits_compute=dp.get_precision('Account')), 'item1A4_ppn': fields.float(string='4. Penyerahan yang PPN-nya tidak dipungut', digits_compute=dp.get_precision('Account')), 'item1A5_dpp': fields.float(string='4. Penyerahan yang dibebankan dari pengenaan PPN', digits_compute=dp.get_precision('Account')), 'item1A5_ppn': fields.float(string='4. Penyerahan yang dibebankan dari pengenaan PPN', digits_compute=dp.get_precision('Account')), 'item_jumlah_1A': fields.float(string='Jumlah (I.A.1 + I.A.2 + I.A.3 + I.A.4 + I.A.5', digits_compute=dp.get_precision('Account')), 'item1B': fields.float(string='Tidak Terutang PPN', digits_compute=dp.get_precision('Account')), 'item1C': fields.float(string='Jumlah Seluruh Penyerahan (I.A + I.B)', digits_compute=dp.get_precision('Account')), # BAGIAN 2 'item2A': fields.float(string='Pajak keluaran yang harus dipungut sendiri', digits_compute=dp.get_precision('Account')), 'item2B': fields.float(string='PPN disetor dimuka dalam masa pajak yang sama', digits_compute=dp.get_precision('Account')), 'item2C': fields.float(string='Pajak masukan yang dapat diperhitungkan', digits_compute=dp.get_precision('Account')), 'item2D': fields.float( string='PPN kurang atau (lebih) bayar (II.A - II.B - II.C)', digits_compute=dp.get_precision('Account')), 'item2E': fields.float( string='PPN kurang atau (lebih) bayar pada SPT yang dibetulkan', digits_compute=dp.get_precision('Account')), 'item2F': fields.float( string= 'PPN kurang atau (lebih) bayar karena pembetulan (II.D - II.E)', digits_compute=dp.get_precision('Account')), # BAGIAN 3 'item3A': fields.float(string='A. Jumlah Dasar Pengenaan Pajak', digits_compute=dp.get_precision('Account')), 'item3B': fields.float(string='B. PPN Terutang', digits_compute=dp.get_precision('Account')), 'item3C_tanggal': fields.date(string='C. Dilunasi Tanggal'), 'item3C_ntpn': fields.char(string='NTPN', size=100), # BAGIAN 4 'item4A': fields.float(string='A. PPN yang wajib dibayar kembali', digits_compute=dp.get_precision('Account')), 'item4B_tanggal': fields.date(string='B. Dilunasi Tanggal'), 'item4B_ntpn': fields.char(string='NTPN', size=100), # BAGIAN 5 'item5A': fields.float(string='A. PPnBM yang harus dipungut kembali', digits_compute=dp.get_precision('Account')), 'item5B': fields.float( string='B. PPnBM disetor dimuka dalam Masa Pajak yang sama', digits_compute=dp.get_precision('Account')), 'item5C': fields.float(string='C. PPnBM kurang atau (lebih) bayar (V.A - V.B)', digits_compute=dp.get_precision('Account')), 'item5D': fields.float( string= 'D. PPnBM kurang atau (lebih) bayar pada SPT yang dibetulkan', digits_compute=dp.get_precision('Account')), 'item5E': fields.float( string= 'E. PPnBM kurang atau (lebih) bayar karena pembetulan (V.C - V.D)', digits_compute=dp.get_precision('Account')), 'item5F_tanggal': fields.date(string='F. Dilunasi Tanggal'), 'item5F_ntpn': fields.char(string='NTPN', size=100), # BAGIAN 6 # PERNYATAAN 'kota_pernyataan': fields.char(string='Kota', size=100, required=True), 'tanggal_pernyataan': fields.date(string='Tanggal', required=True), 'pernyataan_user_id': fields.many2one(string='Pengurus/Kuasa', obj='res.users', required=True), 'jenis_pengurus': fields.selection(string='PKP/Kuasa', selection=[('pkp', 'PKP'), ('kuasa', 'Kuasa')], required=True), 'state': fields.selection([('draft', 'Draft'), ('confirm', 'Waiting For Approval'), ('approve', 'Ready To Process'), ('done', 'Done'), ('cancel', 'Cancel')], 'Status', readonly=True), 'created_time': fields.datetime(string='Created Time', readonly=True), 'created_user_id': fields.many2one(string='Created By', obj='res.users', readonly=True), 'confirmed_time': fields.datetime(string='Confirmed Time', readonly=True), 'confirmed_user_id': fields.many2one(string='Confirmed By', obj='res.users', readonly=True), 'approved_time': fields.datetime(string='Approved Time', readonly=True), 'approved_user_id': fields.many2one(string='Approved By', obj='res.users', readonly=True), 'processed_time': fields.datetime(string='Processed Time', readonly=True), 'processed_user_id': fields.many2one(string='Process By', obj='res.users', readonly=True), 'cancelled_time': fields.datetime(string='Processed Time', readonly=True), 'cancelled_user_id': fields.many2one(string='Process By', obj='res.users', readonly=True), 'cancelled_reason': fields.text(string='Cancelled Reason', readonly=True), } _defaults = { 'name': default_name, 'company_id': default_company_id, 'state': default_state, 'created_time': default_created_time, 'created_user_id': default_created_user_id, } def workflow_action_confirm(self, cr, uid, ids, context={}): for id in ids: self.write(cr, uid, [id], {'state': 'confirm'}) return True def workflow_action_approve(self, cr, uid, ids, context={}): for id in ids: self.write(cr, uid, [id], {'state': 'approve'}) return True def workflow_action_done(self, cr, uid, ids, context={}): for id in ids: self.write(cr, uid, [id], {'state': 'done'}) return True def workflow_action_cancel(self, cr, uid, ids, context={}): for id in ids: self.write(cr, uid, [id], {'state': 'cancel'}) return True def button_action_set_to_draft(self, cr, uid, ids, context={}): for id in ids: if not self.delete_workflow_instance(self, cr, uid, id): return False if not self.create_workflow_instance(self, cr, uid, id): return False return True def button_action_cancel(self, cr, uid, ids, context={}): wkf_service = netsvc.LocalService('workflow') for id in ids: if not self.delete_workflow_instance(self, cr, uid, id): return False if not self.create_workflow_instance(self, cr, uid, id): return False wkf_service.trg_validate(uid, 'pajak.formulir_1111', id, 'button_cancel', cr) return True def log_audit_trail(self, cr, uid, id, event): #TODO: Ticket #31 if state not in [ 'created', 'confirmed', 'approved', 'processed', 'cancelled' ]: raise osv.except_osv(_('Peringatan!'), _('Error pada method log_audit')) return False state_dict = { 'created': 'draft', 'confirmed': 'confirm', 'approved': 'approve', 'processed': 'done', 'cancelled': 'cancel' } val = { '%s_user_id' % (state): uid, '%s_time' % (state): datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'state': state_dict.get(state, False), } self.write(cr, uid, [id], val) return True def delete_workflow_instance(self, cr, uid, id): #TODO: Ticket #32 wkf_service = netsvc.LocalService('workflow') wkf_service.trg_delete(uid, 'pajak.formulir_1111', id, cr) return True def create_workflow_instance(self, cr, uid, id): #TODO: Ticket #33 wkf_service = netsvc.LocalService('workflow') wkf_service.trg_create(uid, 'pajak.formulir_1111', id, cr) return True
cur_obj = self.pool.get('res.currency') #import pdb;pdb.set_trace() for line in self.browse(cr, uid, ids): #price = line.price_unit * (1-(line.discount or 0.0)/100.0) price = round(line.price_unit *line.quantity,3) # taxes = tax_obj.compute_all(cr, uid, line.invoice_line_tax_id, price, line.quantity, product=line.product_id, partner=line.invoice_id.partner_id) taxes = tax_obj.compute_all(cr, uid, line.invoice_line_tax_id, price, line.quantity, product=line.product_id, partner=line.invoice_id.partner_id) #res[line.id] = taxes['total'] res[line.id] = price # if line.invoice_id: # cur = line.invoice_id.currency_id # res[line.id] = cur_obj.round(cr, uid, cur, res[line.id]) return res _columns = { 'quantity2': fields.float('Small Qty', digits_compute= dp.get_precision('Product Unit of Measure')), 'uom_id' : fields.many2one('product.uom', 'Small UoM', ondelete='set null', select=True,required=True), 'disc_tot': fields.float('Disc Total'), 'price_subtotal': fields.function(_amount_line, string='Amount', type="float", digits_compute= dp.get_precision('Account'), store=True), 'qty_func': fields.function(_get_tot_qty,string='Qty Tot'), 'qty': fields.float('Qty', digits_compute= dp.get_precision('Product Unit of Measure')), 'quantity': fields.function(_get_tot_qty,string='Quantity',type="float", digits_compute= dp.get_precision('Product Unit of Measure'), required=True), 'price_unit2': fields.float('Price Unit 2'), 'quantity3' : fields.float('Quantity PO'), } _defaults = { 'uom_id' : _get_uom_id, }
class WizSaleDiscount(models.TransientModel): _name = 'wiz.sale.discount' _description = 'Apply discount to sale orders' name = fields.Char(string='Name') discount_type = fields.Selection(selection=[ ('percent_line', 'Discount rate per line'), ('percent_total', 'Total discount percent'), ('quantity_total', 'Total discount quantity') ], string='Discount type', default='percent_line', required=True) discount_applied = fields.Float( string='Discount applied (%)', digits_compute=dp.get_precision('Sale price')) product_id = fields.Many2one(comodel_name='product.product', string='Discount product') discount_quantity = fields.Float(string='Discount quantity') discount_taxes = fields.Many2many(comodel_name='account.tax', relation='sale_disc2tax_rel', column1='sale_discount_id', column2='tax_id', string='Discount taxes') @api.multi def button_accept(self): orders = self.env['sale.order'].browse( self.env.context.get('active_ids', [])) for order in orders: if order.state != 'draft': raise exceptions.Warning( _('The discount can only be applied when sale orders are in ' '\'Draft\' state.')) if self.discount_type == 'percent_line': [ line.write({'discount': self.discount_applied}) for line in order.order_line ] order.button_dummy() continue if self.discount_type == 'percent_total': if self.discount_applied > 100 or self.discount_applied < 0: raise exceptions.Warning( _('The applied discount must be between 0 and 100')) discount = order.amount_untaxed * (self.discount_applied / 100.0) price_unit = (-self.discount_quantity if self.discount_type == 'quantity_total' else -discount) line = self.env['sale.order.line'].create({ 'order_id': order.id, 'product_id': self.product_id.id, 'name': _('Discount line'), 'price_unit': price_unit, 'tax_id': [(6, 0, [tax.id for tax in self.discount_taxes])] }) order.button_dummy() return {'type': 'ir.actions.act_window_close'} @api.onchange('product_id') def product_id_change(self): if self.product_id: self.discount_taxes = self.product_id.taxes_id
WHERE rel.invoice_id = ANY(%s)""", (list(ids),)) return [i[0] for i in cr.fetchall()] _name = 'sale.order.line' _description = 'Sales Order Line' _columns = { 'order_id': fields.many2one('sale.order', 'Order Reference', required=True, ondelete='cascade', select=True, readonly=True, states={'draft':[('readonly',False)]}), 'name': fields.text('Description', required=True, readonly=True, states={'draft': [('readonly', False)]}), 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of sales order lines."), 'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], change_default=True), 'invoice_lines': fields.many2many('account.invoice.line', 'sale_order_line_invoice_rel', 'order_line_id', 'invoice_id', 'Invoice Lines', readonly=True), 'invoiced': fields.function(_fnct_line_invoiced, string='Invoiced', type='boolean', store={ 'account.invoice': (_order_lines_from_invoice, ['state'], 10), 'sale.order.line': (lambda self,cr,uid,ids,ctx=None: ids, ['invoice_lines'], 10)}), 'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price'), readonly=True, states={'draft': [('readonly', False)]}), 'type': fields.selection([('make_to_stock', 'from stock'), ('make_to_order', 'on order')], 'Procurement Method', required=True, readonly=True, states={'draft': [('readonly', False)]}, help="From stock: When needed, the product is taken from the stock or we wait for replenishment.\nOn order: When needed, the product is purchased or produced."), 'price_subtotal': fields.function(_amount_line, string='Subtotal', digits_compute= dp.get_precision('Account')), 'tax_id': fields.many2many('account.tax', 'sale_order_tax', 'order_line_id', 'tax_id', 'Taxes', readonly=True, states={'draft': [('readonly', False)]}), 'address_allotment_id': fields.many2one('res.partner', 'Allotment Partner',help="A partner to whom the particular product needs to be allotted."), 'product_uom_qty': fields.float('Quantity', digits_compute= dp.get_precision('Product UoS'), required=True, readonly=True, states={'draft': [('readonly', False)]}), 'product_uom': fields.many2one('product.uom', 'Unit of Measure ', required=True, readonly=True, states={'draft': [('readonly', False)]}), 'product_uos_qty': fields.float('Quantity (UoS)' ,digits_compute= dp.get_precision('Product UoS'), readonly=True, states={'draft': [('readonly', False)]}), 'product_uos': fields.many2one('product.uom', 'Product UoS'), 'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount'), readonly=True, states={'draft': [('readonly', False)]}), 'th_weight': fields.float('Weight', readonly=True, states={'draft': [('readonly', False)]}), 'state': fields.selection([('cancel', 'Cancelled'),('draft', 'Draft'),('confirmed', 'Confirmed'),('exception', 'Exception'),('done', 'Done')], 'Status', required=True, readonly=True, help='* The \'Draft\' status is set when the related sales order in draft status. \ \n* The \'Confirmed\' status is set when the related sales order is confirmed. \ \n* The \'Exception\' status is set when the related sales order is set as exception. \
# ('confirm', 'Waiting Approval'), = deducted in budget # ('accepted', 'Approved'), = deducted in budget # ('done', 'Waiting Payment'), = deducted in budget # ('paid', 'Paid'), = deducted in budget + cash #Totals res[company.id]['smart_cash'] = round(res[company.id]['smart_cash'] - res[company.id]['smart_amount_cash'],0) res[company.id]['smart_budget'] = round(res[company.id]['smart_budget'] - res[company.id]['smart_amount_budget'],0) #res[company.id]['smart_cash'] = 0.0 return res _columns = { 'smart_budget': fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart Budget',multi='all',help="Approved invoiced amount.",), # 'smart_cash': fields.function(smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart Budget', # help="Approved invoiced amount.", # multi='all',), 'smart_cash': fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart Cash', help="Free invoiced amount for salary or expenses.", multi='all',), 'prepayment': fields.boolean('Prepayment',help="SMart User: this virtual company can have prepayment smart_cash, SMart Company: this country applies prepayment"), 'prepayment_days': fields.integer('Prepayment Days',help="Leadtime in days before invoiced amount becomes smart_cash (global)"), 'smart_share': fields.float('SMarts Share',digits_compute=dp.get_precision('Account')), 'sale_order_sum_cash': fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart Order sum cash',multi='all',help="Approved invoiced amount.",), 'sale_order_sum_budget':fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart Order sum budget',multi='all',help="Approved invoiced amount.",), 'smart_amount_cash':fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart Amount cash',multi='all',help="Approved invoiced amount.",), 'smart_amount_budget':fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart Amount budget',multi='all',help="Approved invoiced amount.",), 'activity_amount_cash':fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart activity amount cash',multi='all',help="Approved invoiced amount.",), 'activity_amount_budget':fields.function(_smart_cash, type="float", digits_compute=dp.get_precision('Account'), string='SMart activity amount budget',multi='all',help="Approved invoiced amount.",),
class ProductTemplate(models.Model): _inherit = 'product.template' # Column Section coeff1_id = fields.Many2one(comodel_name='product.coefficient', string='Coefficient 1') incl_in_standard_price_1 = fields.Boolean("Include in Standard Price", default=False, help="""If you check this box, this coefficient will be used to calculate the standard price of the product""") coeff2_id = fields.Many2one(comodel_name='product.coefficient', string='Coefficient 2') incl_in_standard_price_2 = fields.Boolean("Include in Standard Price", default=False, help="""If you check this box, this coefficient will be used to calculate the standard price of the product""") coeff3_id = fields.Many2one(comodel_name='product.coefficient', string='Coefficient 3') incl_in_standard_price_3 = fields.Boolean("Include in Standard Price", default=False, help="""If you check this box, this coefficient will be used to calculate the standard price of the product""") coeff4_id = fields.Many2one(comodel_name='product.coefficient', string='Coefficient 4') incl_in_standard_price_4 = fields.Boolean("Include in Standard Price", default=False, help="""If you check this box, this coefficient will be used to calculate the standard price of the product""") coeff5_id = fields.Many2one(comodel_name='product.coefficient', string='Coefficient 5') incl_in_standard_price_5 = fields.Boolean("Include in Standard Price", default=False, help="""If you check this box, this coefficient will be used to calculate the standard price of the product""") coeff6_id = fields.Many2one(comodel_name='product.coefficient', string='Coefficient 6') incl_in_standard_price_6 = fields.Boolean("Include in Standard Price", default=False, help="""If you check this box, this coefficient will be used to calculate the standard price of the product""") coeff7_id = fields.Many2one(comodel_name='product.coefficient', string='Coefficient 7') incl_in_standard_price_7 = fields.Boolean("Include in Standard Price", default=False, help="""If you check this box, this coefficient will be used to calculate the standard price of the product""") coeff8_id = fields.Many2one(comodel_name='product.coefficient', string='Coefficient 8') incl_in_standard_price_8 = fields.Boolean("Include in Standard Price", default=False, help="""If you check this box, this coefficient will be used to calculate the standard price of the product""") coeff9_id = fields.Many2one(comodel_name='product.coefficient', string='Coefficient 9') incl_in_standard_price_9 = fields.Boolean("Include in Standard Price", default=False, help="""If you check this box, this coefficient will be used to calculate the standard price of the product""") base_price = fields.Float( string='Base Price', compute='_compute_base_price', store=True, digits=dp.get_precision('Product Price'), help="Base Price is the Sale Price of your Supplier.\n" "If product is sold by many suppliers, the first one is selected.\n" "If a supplier sell the product with different prices, the bigger" " price is used.\n\n" "If The supplier info belong an end date, the base price will be" " updated nightly, by a cron task.") alternative_base_price_sale = fields.Float( string='Alternative Base Price for Sale Price', help="This alternative base price will be used instead of the Base" " Price, if defined.") alternative_base_price_standard = fields.Float( string='Alternative Base Price for Standard Price', help="This alternative base price will be used instead of the Base" " Price, if defined.") coeff1_inter = fields.Float(string='With Coefficient 1', compute='_compute_coeff1_inter', store=True, multi="coeff_inter_1") coeff2_inter = fields.Float(string='With Coefficient 2', compute='_compute_coeff2_inter', store=True, multi="coeff_inter_2") coeff3_inter = fields.Float(string='With Coefficient 3', compute='_compute_coeff3_inter', store=True, multi="coeff_inter_3") coeff4_inter = fields.Float(string='With Coefficient 4', compute='_compute_coeff4_inter', store=True, multi="coeff_inter_4") coeff5_inter = fields.Float(string='With Coefficient 5', compute='_compute_coeff5_inter', store=True, multi="coeff_inter_5") coeff6_inter = fields.Float(string='With Coefficient 6', compute='_compute_coeff6_inter', store=True, multi="coeff_inter_6") coeff7_inter = fields.Float(string='With Coefficient 7', compute='_compute_coeff7_inter', store=True, multi="coeff_inter_7") coeff8_inter = fields.Float(string='With Coefficient 8', compute='_compute_coeff8_inter', store=True, multi="coeff_inter_8") coeff9_inter = fields.Float(string='With Coefficient 9', compute='_compute_coeff9_inter', store=True, multi="coeff_inter_9", digits=dp.get_precision('Product Price')) coeff1_inter_sp = fields.Float(string='With Supplier Discount Coefficient', compute='_compute_coeff1_inter', store=True, multi="coeff_inter_1") coeff2_inter_sp = fields.Float(string='With Shipping Coefficient', compute='_compute_coeff2_inter', store=True, multi="coeff_inter_2") coeff3_inter_sp = fields.Float(string='With Loss Coefficient', compute='_compute_coeff3_inter', store=True, multi="coeff_inter_3") coeff4_inter_sp = fields.Float(string='With Coefficient 4', compute='_compute_coeff4_inter', store=True, multi="coeff_inter_4") coeff5_inter_sp = fields.Float(string='With Coefficient 5', compute='_compute_coeff5_inter', store=True, multi="coeff_inter_5") coeff6_inter_sp = fields.Float(string='With Coefficient 6', compute='_compute_coeff6_inter', store=True, multi="coeff_inter_6") coeff7_inter_sp = fields.Float(string='With Coefficient 7', compute='_compute_coeff7_inter', store=True, multi="coeff_inter_7") coeff8_inter_sp = fields.Float(string='With Coefficient 8', compute='_compute_coeff8_inter', store=True, multi="coeff_inter_8") coeff9_inter_sp = fields.Float(string='With Margin Coefficient', compute='_compute_coeff9_inter', store=True, multi="coeff_inter_9", digits=dp.get_precision('Product Price')) theoritical_price = fields.Float(string='Theoritical Price VAT Incl.', compute='_compute_theoritical_price', store=True, digits=dp.get_precision('Product Price')) has_theoritical_price_different = fields.Boolean( string='Has Theoritical Price Different', store=True, compute='_compute_has_theoritical_price_different') has_theoritical_cost_different = fields.Boolean( string='Has Theoritical Cost Different', store=True, compute='_compute_has_theoritical_cost_different') # Custom Section @api.multi def recompute_base_price(self): self._compute_base_price() @api.multi def use_theoritical_price(self): for template in self: template.list_price = template.theoritical_price return True @api.multi def use_theoritical_cost(self): for template in self: template.standard_price = template.coeff9_inter_sp return True @api.model def cron_recompute_base_price(self): auto_update_base_price = self.get_auto_update_base_price() if auto_update_base_price: templates = self.search([]) templates.recompute_base_price() # Compute Section @api.multi @api.depends('product_variant_ids', 'uom_id', 'uom_po_id', 'seller_ids.price', 'seller_ids.product_uom', 'seller_ids.discount') def _compute_base_price(self): # TODO IMPME. Compute with discount, depending on # product_supplierinfo_discount product_obj = self.env['product.product'] for template in self: base_price = 0.0 if template.product_variant_ids: # We set a high quantity to avoid to skip seller = product_obj._select_seller( template.product_variant_ids[0], quantity=10000.0) if seller: if seller.product_uom.id == template.uom_id.id: base_price =\ seller.price * (100 - seller.discount) / 100 else: base_price = ( (seller.price / seller.product_uom.factor_inv) * template.uom_id.factor_inv * (100 - seller.discount) / 100) template.base_price = base_price @api.multi @api.depends('alternative_base_price_standard', 'alternative_base_price_sale', 'base_price', 'coeff1_id.operation_type', 'coeff1_id.value', 'incl_in_standard_price_1') def _compute_coeff1_inter(self): coefficient_obj = self.env['product.coefficient'] for template in self: if template.alternative_base_price_sale: base_price_sale = template.alternative_base_price_sale else: base_price_sale = template.base_price if template.alternative_base_price_standard: base_price_standard = template.alternative_base_price_standard else: base_price_standard = template.base_price template.coeff1_inter = coefficient_obj.compute_price( template.coeff1_id, base_price_sale) if template.incl_in_standard_price_1: template.coeff1_inter_sp = coefficient_obj.compute_price( template.coeff1_id, base_price_standard) else: template.coeff1_inter_sp = base_price_standard @api.multi @api.depends('coeff1_inter', 'coeff2_id.operation_type', 'coeff2_id.value', 'incl_in_standard_price_2') def _compute_coeff2_inter(self): coefficient_obj = self.env['product.coefficient'] for template in self: template.coeff2_inter = coefficient_obj.compute_price( template.coeff2_id, template.coeff1_inter) if template.incl_in_standard_price_2: template.coeff2_inter_sp = coefficient_obj.compute_price( template.coeff2_id, template.coeff1_inter_sp) else: template.coeff2_inter_sp = template.coeff1_inter_sp @api.multi @api.depends('coeff2_inter', 'coeff3_id.operation_type', 'coeff3_id.value', 'incl_in_standard_price_3') def _compute_coeff3_inter(self): coefficient_obj = self.env['product.coefficient'] for template in self: template.coeff3_inter = coefficient_obj.compute_price( template.coeff3_id, template.coeff2_inter) if template.incl_in_standard_price_3: template.coeff3_inter_sp = coefficient_obj.compute_price( template.coeff3_id, template.coeff2_inter_sp) else: template.coeff3_inter_sp = template.coeff2_inter_sp @api.multi @api.depends('coeff3_inter', 'coeff4_id.operation_type', 'coeff4_id.value', 'incl_in_standard_price_4') def _compute_coeff4_inter(self): coefficient_obj = self.env['product.coefficient'] for template in self: template.coeff4_inter = coefficient_obj.compute_price( template.coeff4_id, template.coeff3_inter) if template.incl_in_standard_price_4: template.coeff4_inter_sp = coefficient_obj.compute_price( template.coeff4_id, template.coeff3_inter_sp) else: template.coeff4_inter_sp = template.coeff3_inter_sp @api.multi @api.depends('coeff4_inter', 'coeff5_id.operation_type', 'coeff5_id.value', 'incl_in_standard_price_5') def _compute_coeff5_inter(self): coefficient_obj = self.env['product.coefficient'] for template in self: template.coeff5_inter = coefficient_obj.compute_price( template.coeff5_id, template.coeff4_inter) if template.incl_in_standard_price_5: template.coeff5_inter_sp = coefficient_obj.compute_price( template.coeff5_id, template.coeff4_inter_sp) else: template.coeff5_inter_sp = template.coeff4_inter_sp @api.multi @api.depends('coeff5_inter', 'coeff6_id.operation_type', 'coeff6_id.value', 'incl_in_standard_price_6') def _compute_coeff6_inter(self): coefficient_obj = self.env['product.coefficient'] for template in self: template.coeff6_inter = coefficient_obj.compute_price( template.coeff6_id, template.coeff5_inter) if template.incl_in_standard_price_6: template.coeff6_inter_sp = coefficient_obj.compute_price( template.coeff6_id, template.coeff5_inter_sp) else: template.coeff6_inter_sp = template.coeff5_inter_sp @api.multi @api.depends('coeff6_inter', 'coeff7_id.operation_type', 'coeff7_id.value', 'incl_in_standard_price_7') def _compute_coeff7_inter(self): coefficient_obj = self.env['product.coefficient'] for template in self: template.coeff7_inter = coefficient_obj.compute_price( template.coeff7_id, template.coeff6_inter) if template.incl_in_standard_price_7: template.coeff7_inter_sp = coefficient_obj.compute_price( template.coeff7_id, template.coeff6_inter_sp) else: template.coeff7_inter_sp = template.coeff6_inter_sp @api.multi @api.depends('coeff7_inter', 'coeff8_id.operation_type', 'coeff8_id.value', 'incl_in_standard_price_8') def _compute_coeff8_inter(self): coefficient_obj = self.env['product.coefficient'] for template in self: template.coeff8_inter = coefficient_obj.compute_price( template.coeff8_id, template.coeff7_inter) if template.incl_in_standard_price_8: template.coeff8_inter_sp = coefficient_obj.compute_price( template.coeff8_id, template.coeff7_inter_sp) else: template.coeff8_inter_sp = template.coeff7_inter_sp @api.multi @api.depends('coeff8_inter', 'coeff9_id.operation_type', 'coeff9_id.value', 'incl_in_standard_price_9') def _compute_coeff9_inter(self): coefficient_obj = self.env['product.coefficient'] for template in self: template.coeff9_inter = coefficient_obj.compute_price( template.coeff9_id, template.coeff8_inter) if template.incl_in_standard_price_9: template.coeff9_inter_sp = coefficient_obj.compute_price( template.coeff9_id, template.coeff8_inter_sp) else: template.coeff9_inter_sp = template.coeff8_inter_sp @api.multi @api.depends('coeff9_inter', 'taxes_id.amount', 'taxes_id.price_include', 'taxes_id.amount_type') def _compute_theoritical_price(self): for template in self: multi = 1 for tax in template.taxes_id: if tax.amount_type != 'percent' or not tax.price_include: raise exceptions.UserError( _("Unimplemented Feature\n" "The Tax %s is not correctly set for computing" " prices with coefficients for the product %s") % (tax.name, template.name)) multi *= 1 + (tax.amount / 100) template.theoritical_price = template.coeff9_inter * multi @api.multi @api.depends('theoritical_price', 'list_price') def _compute_has_theoritical_price_different(self): auto_update_theorical_price = self.get_auto_update_theorical_price() for template in self: if template.theoritical_price and ( template.base_price or template.alternative_base_price_sale): template.has_theoritical_price_different =\ template.list_price != template.theoritical_price else: template.has_theoritical_price_different = False @api.multi @api.depends('coeff9_inter_sp', 'standard_price') def _compute_has_theoritical_cost_different(self): auto_update_theorical_cost = self.get_auto_update_theorical_cost() for template in self: if template.coeff9_inter_sp and ( template.base_price or template.alternative_base_price_standard): template.has_theoritical_cost_different =\ template.standard_price != template.coeff9_inter_sp else: template.has_theoritical_cost_different = False @api.model def get_auto_update_base_price(self): # Get Purchase Configuration: Updates Base Price automatically param_env = self.env['ir.config_parameter'] val = safe_eval(param_env.get_param('auto_update_base_price')) return val @api.model def get_auto_update_theorical_cost(self): # Get Purchase Configuration: Updates Theorical Cost automatically param_env = self.env['ir.config_parameter'] val = safe_eval(param_env.get_param('auto_update_theorical_cost')) return val @api.model def get_auto_update_theorical_price(self): # Get Purchase Configuration: Updates Theorical Price automatically param_env = self.env['ir.config_parameter'] val = safe_eval(param_env.get_param('auto_update_theorical_price')) return val @api.multi def auto_update_theoritical_cost_price(self): for obj in self: if obj.has_theoritical_cost_different and \ obj.get_auto_update_theorical_cost(): obj.use_theoritical_cost() if obj.has_theoritical_price_different and \ obj.get_auto_update_theorical_price(): obj.use_theoritical_price() @api.multi def write(self, vals): ret = super(ProductTemplate, self).write(vals) self.auto_update_theoritical_cost_price() return ret @api.model def create(self, vals): new_obj = super(ProductTemplate, self).create(vals) new_obj.auto_update_theoritical_cost_price() return new_obj
class delivery_route_line(osv.osv): _inherit = 'delivery.route.line' def _get_sign_paid(self, cr, uid, ids, fields_name, args, context=None): """ Indica si el cliente debe pagar el pedido en caso de no contar con credito y muestra el botón de 'pago' si el estado de la factura es 'abierto """ res = {} date = time.strftime('%Y-%m-%d') # Obtiene la configuracion config_obj = self.pool.get('delivery.config.settings') config = config_obj.get_setting_payment(cr, uid, context=context) # Recorremos los ids for id in ids: res[id] = { 'paid_button_show': False, 'paid_all_sign': False, 'paid_cash_sign': False, } # Valida que apliquen condiciones sobre pago if not config['payments_enable']: continue delivery = self.read( cr, uid, id, ['credit_available', 'date_due', 'invoice_state', 'residual']) print "*****DATE_DUE*****: ", delivery['date_due'] print "*****DATE*****: ", date # Se valida que el cliente disponga del credito para pagar y la factura no este vencida if delivery['credit_available'] == 0.0 and delivery[ 'date_due'] <= date and delivery[ 'credit_available'] < delivery['residual']: res[id]['paid_all_sign'] = True #if type_payment_term == 'cash': # res[id]['paid_cash_sign'] = True #if type_payment_term == 'cash': # self.write(cr, uid, ids, {'state': 'pending_paid'}, context=context) # # Registra el log sobre la actualizacion sobre la transicion de estado # self.pool.get('delivery.route.line.log').add_log_route_line(cr, uid, ids, state='pending_paid', context=context) # Se valida si existe un pago pendiente if delivery['residual'] > 0.0: res[id]['paid_button_show'] = True return res _columns = { 'residual': fields.related('invoice_id', 'residual', type='float', digits_compute=dp.get_precision('Delivery'), string="Monto a pagar"), 'date_invoice': fields.related('invoice_id', 'date_invoice', type='date', string="Fecha factura"), 'date_due': fields.related('invoice_id', 'date_due', type='date', string="Fecha vencimiento"), 'payment_term': fields.related('invoice_id', 'payment_term', type='many2one', relation='account.payment.term', string="Plazo de pago"), 'state': fields.selection( [('draft', 'No planeado'), ('planned', 'Planeado'), ('open', 'Por surtir'), ('pending_paid', 'Esperando pago'), ('arrived', 'Arribado'), ('delivered', 'En entrega'), ('delivered_carrier', 'En entrega transportista'), ('exeption', 'Excepcion entrega'), ('done', 'Entregado'), ('pending_paid_delivered', 'Esperando pago de entrega'), ('pending_credit', 'Autorizacion credito'), ('paid', 'pagado'), ('not_found', 'No encontrado'), ('return', 'Devuelto'), ('picking', 'Entregado Almacen'), ('cancel', 'Cancelado')], string='Estado entrega', store=True, readonly=True), 'credit_available': fields.related('address_id', 'credit_available', type="float", string="Credito disponible", digits_compute=dp.get_precision('Delivery')), 'paid_button_show': fields.function(_get_sign_paid, type='boolean', string="Mostrar boton pago", store=False, multi='paid'), 'paid_all_sign': fields.function(_get_sign_paid, type='boolean', string="Pagar todo", store=False, multi='paid'), 'pending_credit': fields.boolean('solicito credito?') } def action_paid(self, cr, uid, ids, context=None): """ Abre el wizard para realizar el pago además de pasar el pedido al estado 'En entrega' """ #paid_obj = self.pool.get('paid.manager.wizard') move = self.browse(cr, uid, ids[0], context=context) invoice = move.invoice_id residual = move.residual route_id = move.route_id.id or False dummy, view_id = self.pool.get('ir.model.data').get_object_reference( cr, uid, 'delivery_plan', 'paid_manager_wizard') return { 'name': 'Pagar factura', 'view_type': 'form', 'view_mode': 'form', 'view_id': view_id, 'res_model': 'paid.manager.wizard', 'target': 'new', 'context': context, 'type': 'ir.actions.act_window', 'nodestroy': True, #'res_id': paid_id 'context': { #'payment_expected_currency': invoice.currency_id.id, 'default_partner_id': self.pool.get('res.partner')._find_accounting_partner( invoice.partner_id).id, 'default_amount': invoice.type in ('out_refund', 'in_refund') and -residual or residual, 'default_reference': invoice.name, #'close_after_process': True, #'invoice_type': invoice.type, 'default_invoice_id': invoice.id, 'default_route_id': route_id, #'default_type': invoice.type in ('out_invoice','out_refund') and 'receipt' or 'payment', #'type': invoice.type in ('out_invoice','out_refund') and 'receipt' or 'payment' } } def action_arrived(self, cr, uid, ids, context=None): """ Pasa la entrega a estado arrivado validando que el tipo de pago no sea de contado """ type_payment_term = self.browse(cr, uid, ids[0], context=context).payment_term.type #print "******TYPE_PAYMENT_TERM*****: ", type_payment_term super(delivery_route_line, self).action_arrived(cr, uid, ids, context=context) return True #def action_done(self, cr, uid, ids, context=None): # """ # Valida si hay algun adedudo una vez entregado el producto, en caso de haberlo se realiza el cobro # """ # super(delivery_route_line, self). action_done(cr, uid, ids, context=context) # # move = self.browse(cr, uid, ids[0], context=context) # if move.residual > 0: # self.write(cr, uid, ids, {'state': 'pending_paid_delivered'}, context=context) # # Registra el log sobre la actualizacion sobre la transicion de estado # self.pool.get('delivery.route.line.log').add_log_route_line(cr, uid, ids, state='pending_paid_delivered', context=context) # return True # else: # return True def action_requisition_credit_new(self, cr, uid, ids, context=None): """ Permite realizar una solicitud de credito """ if context is None: context = {} #if self.browse(cr,uid,ids[0], context=context).pending_credit == False: # self.write(cr, uid, ids, {'state': 'pending_credit', 'pending_credit': True}, context=context) # # Registra el log sobre la actualizacion sobre la transicion de estado # self.pool.get('delivery.route.line.log').add_log_route_line(cr, uid, ids, state='pending_credit', context=context) # Obtiene la vista a cargar if not ids: return [] dummy, view_id = self.pool.get('ir.model.data').get_object_reference( cr, uid, 'requisition_credit', 'requisition_credit_credit_new_form_view') route_line = self.browse(cr, uid, ids[0], context=context) # Obtiene los parametros que van por default context[ 'default_partner_id'] = route_line.invoice_id.partner_id.id or False context['default_rfc'] = route_line.invoice_id.partner_id.rfc context[ 'default_current_credit'] = route_line.invoice_id.partner_id.credit_limit context['default_user_id'] = uid context['default_state'] = 'open' return { 'name': _("Solicitud de credito"), 'view_mode': 'form', 'view_id': view_id, 'view_type': 'form', 'res_model': 'requisition.credit.credit', 'type': 'ir.actions.act_window', 'nodestroy': True, 'target': 'current', 'domain': '[]', 'context': context }
class account_wh_src(osv.osv): def name_get(self, cursor, user, ids, context=None): """ To generate a name for src record """ if isinstance(ids, (int, long)): ids = [ids] if not ids: return [] res = [] data_move = self.pool.get('account.wh.src').browse( cursor, user, ids, context=context) for move in data_move: if not move.name: if move.number: name = move.number else: name = 'CRS * ID = ' + str(move.id) else: name = move.name res.append((move.id, name)) return res def _get_uid_wh_agent(self, cr, uid, context=None): """ Return true if current partner is social responsability agent and return false in otherwise """ context = context or {} rp_obj = self.pool.get('res.partner') ru_obj = self.pool.get('res.users') ru_brw = ru_obj.browse(cr, uid, uid, context=context) acc_part_brw = rp_obj._find_accounting_partner( ru_brw.company_id.partner_id) return acc_part_brw.wh_src_agent def _get_partner_agent(self, cr, uid, context=None): """ Return a list of browse partner depending of invoice type """ obj_partner = self.pool.get('res.partner') args = [('parent_id', '=', False)] context = context or {} res = [] if context.get('type') in ('out_invoice',): args.append(('wh_src_agent', '=', True)) partner_ids = obj_partner.search(cr, uid, args) if partner_ids: partner_brw = obj_partner.browse( cr, uid, partner_ids, context=context) res = [item.id for item in partner_brw] return res def default_get(self, cr, uid, field_list, context=None): """ Update fields uid_wh_agent and partner_list to the create a record """ # NOTE: use field_list argument instead of fields for fix the pylint # error W0621 Redefining name 'fields' from outer scope context = context or {} res = super(account_wh_src, self).default_get( cr, uid, field_list, context=context) res.update({'uid_wh_agent': self._get_uid_wh_agent( cr, uid, context=context)}) res.update({'partner_list': self._get_partner_agent( cr, uid, context=context)}) return res def _get_p_agent(self, cr, uid, ids, field_name, args, context=None): """ Create a dictionary with ids partner and their browse item """ context = context or {} res = {}.fromkeys(ids, self._get_partner_agent( cr, uid, context=context)) return res def _get_wh_agent(self, cr, uid, ids, field_name, args, context=None): """ Create a dictionary with ids agent partner and their browse item """ context = context or {} res = {}.fromkeys(ids, self._get_uid_wh_agent(cr, uid, context=context)) return res _name = "account.wh.src" _description = "Social Responsibility Commitment Withholding" _columns = { 'name': fields.char('Description', size=64, readonly=True, states={'draft': [('readonly', False)]}, help="Description of withholding"), 'code': fields.char( 'Code', size=32, readonly=True, states={'draft': [('readonly', False)]}, help="Withholding reference"), 'number': fields.char( 'Number', size=32, states={'draft': [('readonly', False)]}, help="Withholding number"), 'type': fields.selection([ ('out_invoice', 'Customer Invoice'), ('in_invoice', 'Supplier Invoice'), ], 'Type', readonly=False, help="Withholding type"), 'state': fields.selection([ ('draft', 'Draft'), ('confirmed', 'Confirmed'), ('done', 'Done'), ('cancel', 'Cancelled') ], 'Estado', readonly=True, help="Status Voucher"), 'date_ret': fields.date('Withholding date', readonly=True, states={'draft': [('readonly', False)]}, help="Keep empty to use the current date"), 'date': fields.date( 'Date', readonly=True, states={'draft': [('readonly', False)]}, help="Date"), 'period_id': fields.many2one( 'account.period', 'Force Period', domain=[('state', '!=', 'done')], readonly=True, states={'draft': [('readonly', False)]}, help="Keep empty to use the period of the validation" " (Withholding date) date."), 'account_id': fields.many2one( 'account.account', 'Account', required=True, readonly=True, states={'draft': [('readonly', False)]}, help="The pay account used for this withholding."), 'partner_id': fields.many2one( 'res.partner', 'Partner', readonly=True, required=True, states={'draft': [('readonly', False)]}, help="Withholding customer/supplier"), 'currency_id': fields.many2one( 'res.currency', 'Currency', required=True, readonly=True, states={'draft': [('readonly', False)]}, help="Currency"), 'journal_id': fields.many2one( 'account.journal', 'Journal', required=True, readonly=True, states={'draft': [('readonly', False)]}, help="Journal entry"), 'company_id': fields.many2one( 'res.company', 'Company', required=True, help="Company"), 'line_ids': fields.one2many( 'account.wh.src.line', 'wh_id', 'Local withholding lines', readonly=True, states={'draft': [('readonly', False)]}, help="Invoices which deductions will be made"), 'wh_amount': fields.float( 'Amount', required=False, digits_compute=dp.get_precision('Withhold'), help="Amount withheld"), 'uid_wh_agent': fields.function( _get_wh_agent, type='boolean', string="uid_wh_agent", store=False, help='indicates whether the current user is agent'), 'partner_list': fields.function( _get_p_agent, type='char', string='Lista', store=False, method=False, help='partners are only allowed to be withholding agents'), } def _get_journal(self, cr, uid, context=None): """ Return a SRC journal depending of invoice type """ context = dict(context or {}) type_inv = context.get('type', 'in_invoice') type2journal = {'out_invoice': 'src_sale', 'in_invoice': 'src_purchase'} journal_obj = self.pool.get('account.journal') user = self.pool.get('res.users').browse( cr, uid, uid, context=context) company_id = context.get('company_id', user.company_id.id) domain = [('company_id', '=', company_id)] domain += [('type', '=', type2journal.get( type_inv, 'src_purchase'))] res = journal_obj.search(cr, uid, domain, limit=1) return res and res[0] or False _defaults = { 'state': lambda *a: 'draft', 'currency_id': lambda self, cr, uid, c: self.pool.get('res.users').browse( cr, uid, uid, c).company_id.currency_id.id, 'journal_id': _get_journal, 'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse( cr, uid, uid, c).company_id.id, } _sql_constraints = [ ] def onchange_partner_id(self, cr, uid, ids, inv_type, partner_id, context=None): """ Return account depending of the invoice @param type: invoice type @param partner_id: partner id """ if context is None: context = {} acc_part_brw = False acc_id = False rp_obj = self.pool.get('res.partner') wh_line_obj = self.pool.get('account.wh.src.line') if partner_id: acc_part_brw = rp_obj._find_accounting_partner( rp_obj.browse(cr, uid, partner_id)) if inv_type in ('out_invoice', 'out_refund'): acc_id = acc_part_brw.property_account_receivable \ and acc_part_brw.property_account_receivable.id or False else: acc_id = acc_part_brw.property_account_payable \ and acc_part_brw.property_account_payable.id or False part_brw = ids and rp_obj._find_accounting_partner(self.browse( cr, uid, ids[0], context=context).partner_id) wh_lines = ids and wh_line_obj.search(cr, uid, [('wh_id', '=', ids[0])]) if not partner_id: if wh_lines: wh_line_obj.unlink(cr, uid, wh_lines) wh_lines = [] if part_brw and acc_part_brw and part_brw.id != acc_part_brw.id: if wh_lines: wh_line_obj.unlink(cr, uid, wh_lines) wh_lines = [] return {'value': { 'line_ids': wh_lines, 'account_id': acc_id, } } def action_date_ret(self, cr, uid, ids, context=None): """ if the retention date is empty, is filled with the current date """ for wh in self.browse(cr, uid, ids, context): if not wh.date_ret: self.write(cr, uid, [wh.id], {'date_ret': time.strftime('%Y-%m-%d')}) return True def action_draft(self, cr, uid, ids, context=None): """ Passes the document to draft status """ context = context or {} inv_obj = self.pool.get('account.invoice') brw = self.browse(cr, uid, ids[0], context) inv_ids = [i.invoice_id.id for i in brw.line_ids] if inv_ids: inv_obj.write(cr, uid, inv_ids, {'wh_src_id': False}) return self.write(cr, uid, ids[0], {'state': 'draft'}) def action_confirm(self, cr, uid, ids, context=None): """ Retention is valid to pass a status confirmed """ context = context or {} inv_obj = self.pool.get('account.invoice') brw = self.browse(cr, uid, ids[0], context) line_ids = brw.line_ids if not line_ids: raise osv.except_osv( _('Invalid Procedure!'), _("No retention lines")) res = [True] res += [False for i in line_ids if (i.wh_amount <= 0.0 or i.base_amount <= 0.0 or i.wh_src_rate <= 0.0)] if not all(res): raise osv.except_osv( _('Invalid Procedure!'), _("Verify retention lines not have Null values(0.00)")) res = 0.0 for i in line_ids: res += i.wh_amount if abs(res - brw.wh_amount) > 0.0001: raise osv.except_osv( _('Invalid Procedure!'), _("Check the amount of withholdings")) inv_ids = [i.invoice_id.id for i in brw.line_ids] if inv_ids: inv_obj.write(cr, uid, inv_ids, {'wh_src_id': ids[0]}) return self.write(cr, uid, ids[0], {'state': 'confirmed'}) def action_done(self, cr, uid, ids, context=None): """ Pass the document to state done """ if context is None: context = {} self.action_date_ret(cr, uid, ids, context=context) self.action_number(cr, uid, ids) self.action_move_create(cr, uid, ids, context=context) return self.write(cr, uid, ids, {'state': 'done'}) def _dummy_cancel_check(self, cr, uid, ids, context=None): ''' This will be the method that another developer should use to create new check on Withholding Document Make super to this method and create your own cases ''' return True def cancel_check(self, cr, uid, ids, context=None): ''' Unique method to check if we can cancel the Withholding Document ''' context = context or {} ids = isinstance(ids, (int, long)) and [ids] or ids if not self._dummy_cancel_check(cr, uid, ids, context=context): return False return True def cancel_move(self, cr, uid, ids, *args): """ Delete move lines related with withholding vat and cancel """ ids = isinstance(ids, (int, long)) and [ids] or ids am_obj = self.pool.get('account.move') for ret in self.browse(cr, uid, ids): if ret.state == 'done': for ret_line in ret.line_ids: if ret_line.move_id: am_obj.button_cancel(cr, uid, [ret_line.move_id.id]) am_obj.unlink(cr, uid, [ret_line.move_id.id]) ret.write({'state': 'cancel'}) return True def clear_wh_lines(self, cr, uid, ids, context=None): """ Clear lines of current withholding document and delete wh document information from the invoice. """ context = context or {} awsl_obj = self.pool.get('account.wh.src.line') ai_obj = self.pool.get('account.invoice') if ids: awsl_ids = awsl_obj.search(cr, uid, [('wh_id', 'in', ids)], context=context) ai_ids = awsl_ids and [ awsl.invoice_id.id for awsl in awsl_obj.browse(cr, uid, awsl_ids, context=context)] if ai_ids: ai_obj.write(cr, uid, ai_ids, {'wh_src_id': False}, context=context) if awsl_ids: awsl_obj.unlink(cr, uid, awsl_ids, context=context) return True def action_cancel(self, cr, uid, ids, context=None): """ Call cancel_move and return True """ ids = isinstance(ids, (int, long)) and [ids] or ids context = context or {} self.cancel_move(cr, uid, ids) self.clear_wh_lines(cr, uid, ids, context=context) return True def copy(self, cr, uid, ids, default=None, context=None): """ Lines can not be duplicated in this model """ # NOTE: use ids argument instead of id for fix the pylint error W0622. # Redefining built-in 'id' raise osv.except_osv( _('Invalid Procedure!'), _("You can not duplicate lines")) def unlink(self, cr, uid, ids, context=None): """ Overwrite the unlink method to throw an exception if the withholding is not in cancel state.""" context = context or {} for src_brw in self.browse(cr, uid, ids, context=context): if src_brw.state != 'cancel': raise osv.except_osv( _("Invalid Procedure!!"), _("The withholding document needs to be in cancel state to" " be deleted.")) else: super(account_wh_src, self).unlink( cr, uid, ids, context=context) return True def action_move_create(self, cr, uid, ids, context=None): """ Build account moves related to withholding invoice """ inv_obj = self.pool.get('account.invoice') if context is None: context = {} context.update({'wh_src': True}) ret = self.browse(cr, uid, ids[0], context) for line in ret.line_ids: if line.move_id: raise osv.except_osv( _('Invoice already withhold !'), _("You must omit the follow invoice '%s' !") % (line.invoice_id.number,)) acc_id = ret.account_id.id period_id = ret.period_id and ret.period_id.id or False journal_id = ret.journal_id.id if not period_id: per_obj = self.pool.get('account.period') period_id = per_obj.find( cr, uid, ret.date_ret or time.strftime('%Y-%m-%d')) # Due to the fact that demo data for periods sets 'special' as True # on them, this little hack is necesary if this issue is solved we # should ask directly for the refer to this bug for more # information # https://bugs.launchpad.net/openobject-addons/+bug/924200 demo_enabled = self.pool.get('ir.module.module').search( cr, uid, [('name', '=', 'base'), ('demo', '=', True)]) args = [('id', 'in', period_id)] if not demo_enabled: args.append(('special', '=', False)) period_id = per_obj.search(cr, uid, args) if not period_id: raise osv.except_osv( _('Missing Periods!'), _("There are not Periods created for the pointed day:" " %s!") % (ret.date_ret or time.strftime('%Y-%m-%d'))) period_id = period_id[0] if period_id: if ret.line_ids: for line in ret.line_ids: writeoff_account_id, writeoff_journal_id = False, False amount = line.wh_amount if line.invoice_id.type in ['in_invoice', 'in_refund']: name = 'COMP. RET. CRS ' + ret.number + ' Doc. ' + ( line.invoice_id.supplier_invoice_number or '') else: name = 'COMP. RET. CRS ' + ret.number + ' Doc. ' + ( line.invoice_id.number or '') ret_move = inv_obj.ret_and_reconcile( cr, uid, [line.invoice_id.id], amount, acc_id, period_id, journal_id, writeoff_account_id, period_id, writeoff_journal_id, ret.date_ret, name, [line], context) rl = { 'move_id': ret_move['move_id'], } lines = [(1, line.id, rl)] self.write(cr, uid, [ret.id], { 'line_ids': lines, 'period_id': period_id}) if (rl and line.invoice_id.type in [ 'out_invoice', 'out_refund']): inv_obj.write( cr, uid, [line.invoice_id.id], {'wh_src_id': ret.id}) else: return False return True def action_number(self, cr, uid, ids, *args): """ Is responsible for generating a number for the document if it does not have one """ obj_ret = self.browse(cr, uid, ids)[0] if obj_ret.type == 'in_invoice': cr.execute( 'SELECT id, number ' 'FROM account_wh_src ' 'WHERE id IN (' + ','.join([str(item) for item in ids]) + ')') for (aws_id, number) in cr.fetchall(): if not number: number = self.pool.get('ir.sequence').get( cr, uid, 'account.wh.src.%s' % obj_ret.type) cr.execute('UPDATE account_wh_src SET number=%s ' 'WHERE id=%s', (number, aws_id)) return True def wh_src_confirmed(self, cr, uid, ids): """ Confirm src document """ return True
class account_wh_src_line(osv.osv): _name = "account.wh.src.line" _description = "Social Responsibility Commitment Withholding Line" _columns = { 'name': fields.char( 'Description', size=64, required=True, help="Local Withholding line Description"), 'wh_id': fields.many2one( 'account.wh.src', 'Local withholding', ondelete='cascade', help="Local withholding"), 'invoice_id': fields.many2one( 'account.invoice', 'Invoice', required=True, ondelete='set null', help="Withholding invoice"), 'base_amount': fields.float( 'Base Amount', digits_compute=dp.get_precision('Base Amount to be Withheld'), help='amount to be withheld'), 'wh_amount': fields.float( 'Withheld Amount', digits_compute=dp.get_precision('Withhold'), help='withheld amount'), 'move_id': fields.many2one( 'account.move', 'Account Entry', readonly=True, help="Account Entry"), 'wh_src_rate': fields.float( 'Withholding Rate', help="Withholding rate"), } _defaults = { } _sql_constraints = [ ] def onchange_invoice_id(self, cr, uid, ids, inv_type, invoice_id=False, base_amount=0.0, wh_src_rate=5.0, context=None): """ Change src information to change the invoice @param type: invoice type @param invoice_id: new invoice id @param base_amount: new base amount @param wh_src_rate: new rate of the withhold src """ if context is None: context = {} res = {} inv_obj = self.pool.get('account.invoice') if not invoice_id: return {'value': { 'invoice_id': False, 'base_amount': 0.0, 'wh_src_rate': 0.0, 'wh_amount': 0.0, } } inv_brw = inv_obj.browse(cr, uid, invoice_id) base_amount = base_amount or inv_brw.amount_untaxed wh_src_rate = wh_src_rate or inv_brw.wh_src_rate or 5.0 wh_amount = base_amount * wh_src_rate / 100.0 res = {'value': { 'base_amount': base_amount, 'wh_src_rate': wh_src_rate, 'wh_amount': wh_amount, } } return res
from bokeh.plotting import figure from bokeh.embed import components from bokeh.models import Legend, ColumnDataSource, LabelSet except (ImportError, IOError) as err: _logger.debug(err) OPERATORS = { '<': py_operator.lt, '>': py_operator.gt, '<=': py_operator.le, '>=': py_operator.ge, '==': py_operator.eq, '!=': py_operator.ne } UNIT = dp.get_precision('Product Unit of Measure') _PRIORITY_LEVEL = [('1_red', 'Red'), ('2_yellow', 'Yellow'), ('3_green', 'Green')] class StockWarehouseOrderpoint(models.Model): _inherit = 'stock.warehouse.orderpoint' _description = "Stock Buffer" @api.multi @api.depends("dlt", "adu", "buffer_profile_id.lead_time_id.factor", "buffer_profile_id.variability_id.factor", "product_uom.rounding") def _compute_red_zone(self): for rec in self:
class AccountInvoiceCyC(models.Model): _name = 'account.invoice.cyc' _auto = False MONTHS = [(1, 'January'), (2, 'February'), (3, 'March'), (4, 'April'), (5, 'May'), (6, 'June'), (7, 'July'), (8, 'August'), (9, 'September'), (10, 'October'), (11, 'November'), (12, 'December')] country_id = fields.Many2one('res.country', 'Country', readonly=True) credit_covered = fields.Float('Credit covered', readonly=True, digits_compute=dp.get_precision('Account')) credit_not_covered = fields.Float( 'Credit not covered', readonly=True, digits_compute=dp.get_precision('Account')) not_credit = fields.Float('No credit', readonly=True, digits_compute=dp.get_precision('Account')) cash = fields.Float('Cash', readonly=True, digits_compute=dp.get_precision('Account')) invoice_year = fields.Integer('Year', readonly=True) invoice_month = fields.Selection(MONTHS, string="Month", readonly=True) amount_total = fields.Float('Total', readonly=True, digits_compute=dp.get_precision('Account')) invoice_state = fields.Selection([('open', 'Open'),('paid', 'Paid')], string="Invoice state", readonly=True) def init(self, cr): tools.drop_view_if_exists(cr, self._table) cr.execute("""CREATE VIEW account_invoice_cyc as ( select min(a.id) as id, a.country_id, sum(a.credit_covered) as credit_covered, sum(a.credit_not_covered) as credit_not_covered, sum(a.not_credit) as not_credit, sum(a.cash) as cash, a.invoice_year, a.invoice_month, a.state as invoice_state, sum(a.credit_covered) + sum(a.credit_not_covered)+ sum(a.not_credit) + sum(a.cash) as amount_total from (select min(ai.id) as id, p.country_id, SUM(ai.amount_total) as credit_covered, 0.0 as credit_not_covered, 0.0 as not_credit, 0.0 as cash, extract(year from ai.date_invoice) as invoice_year, extract(month from ai.date_invoice) as invoice_month, ai.state from account_invoice ai inner join res_partner p on p.id = ai.partner_id left join payment_mode pm on pm.id = ai.payment_mode_id left join account_journal aj on aj.id = pm.journal where p.credit_limit > 0 and p.risk_insurance_grant_date is not null and (ai.payment_mode_id is null or aj.type != 'cash') and ai.type = 'out_invoice' and ai.state in ('open', 'paid') group by p.country_id, extract(year from ai.date_invoice), extract(month from ai.date_invoice), ai.state union select min(ai.id) as id, p.country_id, -SUM(ai.amount_total) as credit_covered, 0.0 as credit_not_covered, 0.0 as not_credit, 0.0 as cash, extract(year from ai.date_invoice) as invoice_year, extract(month from ai.date_invoice) as invoice_month, ai.state from account_invoice ai inner join res_partner p on p.id = ai.partner_id left join payment_mode pm on pm.id = ai.payment_mode_id left join account_journal aj on aj.id = pm.journal where p.credit_limit > 0 and p.risk_insurance_grant_date is not null and (ai.payment_mode_id is null or aj.type != 'cash') and ai.type = 'out_refund' and ai.state in ('open', 'paid') group by p.country_id, extract(year from ai.date_invoice), extract(month from ai.date_invoice), ai.state union select min(ai.id) as id, p.country_id, 0.0 as credit_covered, SUM(ai.amount_total) as credit_not_covered, 0.0 as not_credit, 0.0 as cash, extract(year from ai.date_invoice) as invoice_year, extract(month from ai.date_invoice) as invoice_month, ai.state from account_invoice ai inner join res_partner p on p.id = ai.partner_id left join payment_mode pm on pm.id = ai.payment_mode_id left join account_journal aj on aj.id = pm.journal where p.credit_limit > 0 and p.risk_insurance_grant_date is null and (ai.payment_mode_id is null or aj.type != 'cash') and ai.type = 'out_invoice' and ai.state in ('open', 'paid') group by p.country_id, extract(year from ai.date_invoice), extract(month from ai.date_invoice), ai.state union select min(ai.id) as id, p.country_id, 0.0 as credit_covered, -SUM(ai.amount_total) as credit_not_covered, 0.0 as not_credit, 0.0 as cash, extract(year from ai.date_invoice) as invoice_year, extract(month from ai.date_invoice) as invoice_month, ai.state from account_invoice ai inner join res_partner p on p.id = ai.partner_id left join payment_mode pm on pm.id = ai.payment_mode_id left join account_journal aj on aj.id = pm.journal where p.credit_limit > 0 and p.risk_insurance_grant_date is null and (ai.payment_mode_id is null or aj.type != 'cash') and ai.type = 'out_refund' and ai.state in ('open', 'paid') group by p.country_id, extract(year from ai.date_invoice), extract(month from ai.date_invoice), ai.state union select min(ai.id) as id, p.country_id, 0.0 as credit_covered, 0.0 as credit_not_covered, SUM(ai.amount_total) as not_credit, 0.0 as cash, extract(year from ai.date_invoice) as invoice_year, extract(month from ai.date_invoice) as invoice_month, ai.state from account_invoice ai inner join res_partner p on p.id = ai.partner_id left join payment_mode pm on pm.id = ai.payment_mode_id left join account_journal aj on aj.id = pm.journal where p.credit_limit <= 0 and (ai.payment_mode_id is null or aj.type != 'cash') and ai.type = 'out_invoice' and ai.state in ('open', 'paid') group by p.country_id, extract(year from ai.date_invoice), extract(month from ai.date_invoice), ai.state union select min(ai.id) as id, p.country_id, 0.0 as credit_covered, 0.0 as credit_not_covered, -SUM(ai.amount_total) as not_credit, 0.0 as cash, extract(year from ai.date_invoice) as invoice_year, extract(month from ai.date_invoice) as invoice_month, ai.state from account_invoice ai inner join res_partner p on p.id = ai.partner_id left join payment_mode pm on pm.id = ai.payment_mode_id left join account_journal aj on aj.id = pm.journal where p.credit_limit <= 0 and (ai.payment_mode_id is null or aj.type != 'cash') and ai.type = 'out_refund' and ai.state in ('open', 'paid') group by p.country_id, extract(year from ai.date_invoice), extract(month from ai.date_invoice), ai.state union select min(ai.id) as id, p.country_id, 0.0 as credit_covered, 0.0 as credit_not_covered, 0.0 as not_credit, SUM(ai.amount_total) as cash, extract(year from ai.date_invoice) as invoice_year, extract(month from ai.date_invoice) as invoice_month, ai.state from account_invoice ai inner join res_partner p on p.id = ai.partner_id left join payment_mode pm on pm.id = ai.payment_mode_id left join account_journal aj on aj.id = pm.journal where aj.type = 'cash' and ai.type = 'out_invoice' and ai.state in ('open', 'paid') group by p.country_id, extract(year from ai.date_invoice), extract(month from ai.date_invoice), ai.state union select min(ai.id) as id, p.country_id, 0.0 as credit_covered, 0.0 as credit_not_covered, 0.0 as not_credit, -SUM(ai.amount_total) as cash, extract(year from ai.date_invoice) as invoice_year, extract(month from ai.date_invoice) as invoice_month, ai.state from account_invoice ai inner join res_partner p on p.id = ai.partner_id left join payment_mode pm on pm.id = ai.payment_mode_id left join account_journal aj on aj.id = pm.journal where aj.type = 'cash' and ai.type = 'out_refund' and ai.state in ('open', 'paid') group by p.country_id, extract(year from ai.date_invoice), extract(month from ai.date_invoice), ai.state) a group by a.country_id, a.invoice_month, a.invoice_year, a.state) """)
class kg_fettling_invoice(osv.osv): _name = "kg.fettling.invoice" _description = "Fettling Invoice" _order = "entry_date desc" def _amount_all(self, cr, uid, ids, field_name, arg, context=None): res = {} cur_obj = self.pool.get('res.currency') for order in self.browse(cr, uid, ids, context=context): res[order.id] = { 'amount_untaxed': 0.0, 'total_discount': 0.0, 'amount_tax': 0.0, 'total_amt': 0.0, 'amount_total': 0.0, 'payable_amt': 0.0, 'additional_charges': 0.0, 'advance_amt': 0.0, } tax_amt = discount_value = final_other_charges = advance_net_amt = 0.00 total_value_amt = 0.00 for line in order.line_ids: total_value_amt += line.total_value for item in order.line_ids_a: final_other_charges += item.expense_amt for line in order.line_ids_b: advance_net_amt += line.current_adv_amt if order.discount > 0: discount = order.discount else: discount = (total_value_amt * order.discount_per) / 100 price_amt_val = total_value_amt - discount val = 0.00 for c in self.pool.get('account.tax').compute_all( cr, uid, order.tax_id, price_amt_val, 1, 1, order.contractor_id)['taxes']: val += c.get('amount', 0.0) print "valvalval", val tax_amt = val if order.discount_per > 0.00: discount_value = (total_value_amt / 100.00) * order.discount_per else: discount_value = order.discount res[order.id]['amount_untaxed'] = total_value_amt res[order.id]['total_discount'] = discount_value res[order.id]['amount_tax'] = tax_amt res[order.id]['additional_charges'] = final_other_charges res[order.id]['advance_amt'] = advance_net_amt res[order.id][ 'total_amt'] = final_other_charges + total_value_amt + tax_amt res[order.id]['amount_total'] = ( final_other_charges + total_value_amt + tax_amt + order.round_off_amt) - (discount_value + advance_net_amt) res[order.id]['payable_amt'] = ( final_other_charges + total_value_amt + tax_amt + order.round_off_amt) - (discount_value + advance_net_amt) return res _columns = { ## Version 0.1 ## Basic Info 'name': fields.char('Invoice No', size=24, select=True, readonly=True), 'entry_date': fields.date('Invoice Date', required=True), 'note': fields.text('Notes'), 'state': fields.selection([('draft', 'Draft'), ('confirmed', 'Confirmed'), ('cancel', 'Cancelled'), ('approved', 'AC ACK Pending'), ('done', 'AC ACK Done')], 'Status', readonly=True), 'entry_mode': fields.selection([('auto', 'Auto'), ('manual', 'Manual')], 'Entry Mode', readonly=True), 'flag_sms': fields.boolean('SMS Notification'), 'flag_email': fields.boolean('Email Notification'), 'flag_spl_approve': fields.boolean('Special Approval'), 'narration': fields.char('Narration'), ### Entry Info #### 'company_id': fields.many2one('res.company', 'Company Name', readonly=True), 'active': fields.boolean('Active'), 'crt_date': fields.datetime('Creation Date', readonly=True), 'user_id': fields.many2one('res.users', 'Created By', readonly=True), 'confirm_date': fields.datetime('Confirmed Date', readonly=True), 'confirm_user_id': fields.many2one('res.users', 'Confirmed By', readonly=True), 'ap_rej_date': fields.datetime('Approved Date', readonly=True), 'done_date': fields.datetime('Done Date', readonly=True), 'done_user_id': fields.many2one('res.users', 'Done By', readonly=True), 'ap_rej_user_id': fields.many2one('res.users', 'Approved By', readonly=True), 'cancel_date': fields.datetime('Cancelled Date', readonly=True), 'cancel_user_id': fields.many2one('res.users', 'Cancelled By', readonly=True), 'update_date': fields.datetime('Last Updated Date', readonly=True), 'update_user_id': fields.many2one('res.users', 'Last Updated By', readonly=True), 'cancel_remark': fields.text('Cancel'), 'reject_remark': fields.text('Reject'), ## Module Requirement Info 'contractor_id': fields.many2one( 'res.partner', 'Subcontractor', required=True, domain="[('contractor','=','t'),('partner_state','=','approve')]"), 'phone': fields.char('Phone', size=64), 'contact_person': fields.char('Contact Person', size=128), 'inward_fettling_line_ids': fields.many2many( 'ch.fettling.inward.line', 'm2m_fettling_invoice_details', 'order_id', 'inward_id', 'SC Items', domain= "[('flag_invoice','=',False),('contractor_id','=',contractor_id)]" ), 'division_id': fields.many2one('kg.division.master', 'Division', required=True, domain="[('state','=','approved')]"), ## Calculation process Start now 'con_invoice_no': fields.char('Contractor Invoice No', size=128, required=True), 'invoice_date': fields.date('Contractor Invoice Date', required=True), 'due_date': fields.date('Due Date', required=True), 'invoice_amt': fields.float('Contractor Invoice Amount', required=True), 'invoice_copy': fields.binary('Contractor Invoice Copy'), 'filename': fields.char('File Name'), 'tax_id': fields.many2many('account.tax', 'fettling_invoice_taxes', 'invoice_id', 'tax_id', 'Taxes'), 'discount': fields.float('Discount Amount'), 'discount_per': fields.float('Discount(%)'), 'discount_flag': fields.boolean('Discount Flag'), 'discount_per_flag': fields.boolean('Discount Amount Flag'), # Invoice Total and Tax amount calculation 'amount_untaxed': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Untaxed Amount', store=True, multi="sums", help="Untaxed Amount"), 'total_discount': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Discount Amount(-)', store=True, multi="sums", help="Discount Amount"), 'amount_tax': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Tax Amount', store=True, multi="sums", help="Tax Amount"), 'total_amt': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Total Amount', store=True, multi="sums", help="Total Amount"), 'amount_total': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Net Amount', store=True, multi="sums", help="Net Amount"), 'payable_amt': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Payable Amount', store=True, multi="sums", help="Payable Amount"), 'additional_charges': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Additional Charges', store=True, multi="sums", help="Additional Charges"), 'advance_amt': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Adjected Advance Amount(-)', multi="sums", store=True, readonly=True), 'round_off_amt': fields.float('Round off(+/-)'), ##Accounts Process 'balance_receivable': fields.float('Balance Receivable'), 'accounts_state': fields.selection([('pending', 'Pending'), ('paid', 'Paid')], 'Accounts State', readonly=True), ## Child Tables Declaration 'line_ids': fields.one2many('ch.fettling.invoice.line', 'header_id', "Line Details"), 'line_ids_a': fields.one2many('ch.fettling.invoice.expense.track', 'header_id', "Expense Track"), 'line_ids_b': fields.one2many('ch.foundry.advance.details', 'header_id', "Advance Details"), } _defaults = { 'company_id': lambda self, cr, uid, c: self.pool.get('res.company'). _company_default_get(cr, uid, 'kg_fettling_invoice', context=c), 'entry_date': lambda *a: time.strftime('%Y-%m-%d'), 'user_id': lambda obj, cr, uid, context: uid, 'crt_date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), 'state': 'draft', 'active': True, 'entry_mode': 'manual', 'accounts_state': 'pending', 'flag_sms': False, 'flag_email': False, 'flag_spl_approve': False, 'discount_flag': False, 'discount_per_flag': False, } def onchange_discount_value(self, cr, uid, ids, invoice_amt, discount_per): discount_value = invoice_amt * discount_per / 100.00 if discount_per: return {'value': {'discount_flag': True}} else: return {'value': {'discount_flag': False}} def onchange_discount_percent(self, cr, uid, ids, invoice_amt, discount): if discount: discount = discount + 0.00 amt_to_per = (discount / (invoice_amt or 1.0)) * 100.00 return {'value': {'discount_per_flag': True}} else: return {'value': {'discount_per_flag': False}} def button_dummy(self, cr, uid, ids, context=None): return True def _future_entry_date_check(self, cr, uid, ids, context=None): rec = self.browse(cr, uid, ids[0]) today = date.today() today = str(today) today = datetime.strptime(today, '%Y-%m-%d') entry_date = rec.entry_date entry_date = str(entry_date) entry_date = datetime.strptime(entry_date, '%Y-%m-%d') if entry_date > today or entry_date < today: return False return True _constraints = [ #~ (_future_entry_date_check, 'System not allow to save with future and past date. !!',['Invoice Date']), ] def onchange_contractor(self, cr, uid, ids, contractor_id): value = {'contact_person': '', 'phone': ''} if contractor_id: contractor_rec = self.pool.get('res.partner').browse( cr, uid, contractor_id) value = { 'contact_person': contractor_rec.contact_person, 'phone': contractor_rec.phone } return {'value': value} def update_line_items(self, cr, uid, ids, context=None): entry = self.browse(cr, uid, ids[0]) invoice_line_obj = self.pool.get('ch.fettling.invoice.line') del_sql = """ delete from ch_fettling_invoice_line where header_id=%s """ % ( ids[0]) cr.execute(del_sql) for item in entry.inward_fettling_line_ids: vals = { 'header_id': entry.id, 'fettling_id': item.fettling_id.id, 'pattern_id': item.pattern_id.id, 'pattern_code': item.pattern_code, 'pattern_name': item.pattern_name, 'moc_id': item.moc_id.id, 'stage_id': item.stage_id.id, 'stage_name': item.stage_name, 'pour_id': item.pour_id.id, 'pour_line_id': item.pour_line_id.id, 'pump_model_id': item.pump_model_id.id, 'order_bomline_id': item.order_bomline_id.id, 'order_id': item.order_id.id, 'order_line_id': item.order_line_id.id, 'allocation_id': item.allocation_id.id, 'schedule_id': item.schedule_id.id, 'schedule_line_id': item.schedule_line_id.id, 'qty': item.qty, 'actual_qty': item.qty, 'com_weight': item.each_weight, 'seq_no': item.seq_no, 'sub_inward_id': item.header_id.id, 'sub_inward_line_id': item.id, 'sub_wo_line_id': item.sub_wo_line_id.id, 'com_moc_stage_id': [(6, 0, [x.id for x in item.com_moc_stage_id])], } invoice_line_id = invoice_line_obj.create(cr, uid, vals) return True def entry_confirm(self, cr, uid, ids, context=None): entry = self.browse(cr, uid, ids[0]) final_other_charges = 0.00 for item in entry.line_ids_a: final_other_charges += item.expense_amt ### Sequence Number Generation ### if len(entry.line_ids) == 0: raise osv.except_osv(_('Invoice details is must !!'), _('Enter the proceed button!!')) if entry.state == 'draft': sc_inward_line_obj = self.pool.get('ch.fettling.inward.line') if (entry.invoice_amt + final_other_charges) > entry.amount_total: raise osv.except_osv( _('Invoice Amount Exceed!!'), _('System not allow to Invoice Amount grether than Net Amount !!' )) if (entry.invoice_amt + final_other_charges) != entry.amount_total: raise osv.except_osv( _('Invoice Amount !!'), _('System allow to Invoice Amount is Equal to Net amount !!' )) for line_item in entry.line_ids: if line_item.qty == 0: raise osv.except_osv( _('Warning!'), _('System not allow to save Zero values !!')) sc_inward_line_obj.write(cr, uid, line_item.sub_inward_line_id.id, { 'flag_invoice': True, }) if entry.name == '' or entry.name == False: sc_invoice_name = '' sc_invoice_seq_id = self.pool.get('ir.sequence').search( cr, uid, [('code', '=', 'kg.fettling.invoice')]) rec = self.pool.get('ir.sequence').browse( cr, uid, sc_invoice_seq_id[0]) cr.execute("""select generatesequenceno(%s,'%s','%s') """ % (sc_invoice_seq_id[0], rec.code, entry.entry_date)) sc_invoice_name = cr.fetchone() sc_invoice_name = sc_invoice_name[0] else: sc_invoice_name = entry.name self.write( cr, uid, ids, { 'state': 'confirmed', 'name': sc_invoice_name, 'confirm_user_id': uid, 'confirm_date': time.strftime('%Y-%m-%d %H:%M:%S') }) return True def entry_cancel(self, cr, uid, ids, context=None): rec = self.browse(cr, uid, ids[0]) if rec.cancel_remark: self.write( cr, uid, ids, { 'state': 'cancel', 'cancel_user_id': uid, 'cancel_date': time.strftime('%Y-%m-%d %H:%M:%S') }) else: raise osv.except_osv( _('Cancel remark is must !!'), _('Enter the remarks in Cancel remarks field !!')) return True def entry_reject(self, cr, uid, ids, context=None): rec = self.browse(cr, uid, ids[0]) if rec.state == 'approved': if rec.reject_remark: self.write( cr, uid, ids, { 'state': 'confirmed', 'update_user_id': uid, 'update_date': time.strftime('%Y-%m-%d %H:%M:%S') }) else: raise osv.except_osv( _('Reject remark is must !!'), _('Enter the remarks in Reject remarks field !!')) return True def entry_approved(self, cr, uid, ids, context=None): entry = self.browse(cr, uid, ids[0]) final_other_charges = 0.00 for item in entry.line_ids_a: final_other_charges += item.expense_amt if len(entry.line_ids) == 0: raise osv.except_osv(_('Invoice details is must !!'), _('Enter the proceed button!!')) if entry.state == 'confirmed': if (entry.invoice_amt + final_other_charges) > entry.amount_total: raise osv.except_osv( _('Invoice Amount Exceed!!'), _('System not allow to Invoice Amount grether than Net Amount !!' )) if (entry.invoice_amt + final_other_charges) != entry.amount_total: raise osv.except_osv( _('Invoice Amount !!'), _('System allow to Invoice Amount is Equal to Net amount !!' )) self.write( cr, uid, ids, { 'state': 'approved', 'ap_rej_user_id': uid, 'ap_rej_date': time.strftime('%Y-%m-%d %H:%M:%S') }) else: pass return True def entry_accept(self, cr, uid, ids, context=None): rec = self.browse(cr, uid, ids[0]) journal_obj = self.pool.get('account.journal') journal_ids = self.pool.get('account.journal').search( cr, uid, [('type', '=', 'purchase')]) if journal_ids == []: raise osv.except_osv( _('Book Configuration Warning !!'), _('Type is purchase book should be created !!')) journal_rec = self.pool.get('account.journal').browse( cr, uid, journal_ids[0]) if rec.state == 'approved': ## Advance code added start ## adjusted_amt = 0.00 balance_amt = 0.00 cus_adv_obj = self.pool.get('kg.subcontract.advance') cus_adv_inv_obj = self.pool.get('ch.foundry.advance.details') for line in rec.line_ids_b: print "line.order_id", line.order_id adv_ids = self.pool.get('kg.subcontract.advance').search( cr, uid, [('fou_wo_id', '=', line.order_id.id)]) print "adv_ids", adv_ids adv_rec = self.pool.get('kg.subcontract.advance').browse( cr, uid, adv_ids[0]) adjusted_amt = adv_rec.adjusted_amt + line.current_adv_amt balance_amt = line.current_adv_amt - adjusted_amt cus_adv_obj.write(cr, uid, line.sub_advance_id.id, { 'adjusted_amt': adjusted_amt, 'balance_amt': balance_amt }) ## Advance code added end ## total_value_amt = 0.00 for line in rec.line_ids: total_value_amt += line.total_value self.write( cr, uid, ids, { 'balance_receivable': rec.amount_total, 'state': 'done', 'done_user_id': uid, 'done_date': time.strftime('%Y-%m-%d %H:%M:%S') }) ## Account Posting Process Start vou_obj = self.pool.get('account.voucher') move_vals = { 'name': rec.name, 'journal_id': journal_rec.id, 'narration': rec.narration, 'source_id': rec.id, 'date': rec.entry_date, 'division_id': rec.division_id.id, 'trans_type': 'FI', } move_id = vou_obj.create_account_move(cr, uid, move_vals) if rec.contractor_id: account_id = rec.contractor_id.property_account_payable.id if not account_id: raise osv.except_osv( _('Contractor Configuration Warning !!'), _('Contractor account should be configured !!')) credit = rec.amount_total debit = 0.00 move_line_vals = { 'move_id': move_id, 'account_id': account_id, 'credit': credit, 'debit': debit, 'journal_id': journal_rec.id, 'date': rec.entry_date, 'name': rec.name, } move_line_id = vou_obj.create_account_move_line( cr, uid, move_line_vals) for expense in rec.line_ids_a: ex_account_id = expense.expense.account_id.id if not ex_account_id: raise osv.except_osv( _('Expense Configuration Warning !!'), _('Expense account should be configured !!')) move_line_vals = { 'move_id': move_id, 'account_id': ex_account_id, 'credit': 0.00, 'debit': expense.amount, 'journal_id': journal_rec.id, 'date': rec.entry_date, 'name': rec.name, } move_line_id = vou_obj.create_account_move_line( cr, uid, move_line_vals) tax_sql = """ select sub_query.acc_col_id,sum(sub_query.debit) as debit from ( select ac_tax.account_collected_id as acc_col_id, sum((line_exp.amount * ac_tax.amount)) as debit from fettling_invoice_expense_taxe line_tax left join ch_fettling_invoice_expense_track line_exp on(line_exp.id=line_tax.invoice_id) left join account_tax ac_tax on(ac_tax.id=line_tax.tax_id) left join kg_fettling_invoice inv on(inv.id=line_exp.header_id) where line_exp.header_id = %s group by 1 union all select ac_tax.account_collected_id as acc_col_id, sum(((inv.amount_untaxed - inv.total_discount) * ac_tax.amount)) as debit from fettling_invoice_taxes line_tax left join kg_fettling_invoice inv on(inv.id=line_tax.invoice_id) left join account_tax ac_tax on(ac_tax.id=line_tax.tax_id) where inv.id = %s group by 1) as sub_query group by 1""" % (rec.id, rec.id) cr.execute(tax_sql) data = cr.dictfetchall() for vals in data: if vals['acc_col_id'] is None: raise osv.except_osv( _('Account Configuration Warning !!'), _('Tax account should be configured !!')) move_line_vals = { 'move_id': move_id, 'account_id': vals['acc_col_id'], 'credit': 0.00, 'debit': vals['debit'], 'journal_id': journal_rec.id, 'date': rec.entry_date, 'name': rec.name, } move_line_id = vou_obj.create_account_move_line( cr, uid, move_line_vals) discount = 0.00 if rec.amount_untaxed > 0: discount = 0.00 if rec.discount > 0: discount = rec.discount else: discount = (total_value_amt * rec.discount_per) / 100 account_ids = self.pool.get('account.account').search( cr, uid, [('code', '=', 'CON INV')]) if account_ids == []: raise osv.except_osv( _('Account Configuration Warning !!'), _('code name is CON INV account should be created !!')) account_rec = self.pool.get('account.account').browse( cr, uid, account_ids[0]) account_id = account_rec.id if not account_id: raise osv.except_osv( _('Invoice Configuration Warning !!'), _('Invoice account should be configured !!')) credit = 0.00 debit = (rec.amount_untaxed + rec.round_off_amt) - discount move_line_vals = { 'move_id': move_id, 'account_id': account_id, 'credit': credit, 'debit': debit, 'journal_id': journal_rec.id, 'date': rec.entry_date, 'name': rec.name, } move_line_id = vou_obj.create_account_move_line( cr, uid, move_line_vals) return True else: pass def load_advance(self, cr, uid, ids, context=None): invoice_rec = self.browse(cr, uid, ids[0]) cus_adv_obj = self.pool.get('kg.subcontract.advance') cus_inadv_obj = self.pool.get('ch.foundry.advance.details') del_sql = """delete from ch_foundry_advance_details where header_id=%s""" % ( ids[0]) cr.execute(del_sql) for item in [x.id for x in invoice_rec.inward_fettling_line_ids]: work_rec_obj = self.pool.get('ch.fettling.inward.line').browse( cr, uid, item) adv_search = self.pool.get('kg.subcontract.advance').search( cr, uid, [('fou_wo_id', '=', work_rec_obj.sub_wo_line_id.header_id.id)]) cr.execute( """ select * from kg_subcontract_advance where fou_wo_id = %s and balance_amt > 0 and state='confirmed'""" % (work_rec_obj.sub_wo_line_id.header_id.id)) adv_data = cr.dictfetchall() for adv in adv_data: print "adv['order_id']0", adv['fou_wo_id'] cus_inadv_obj.create( cr, uid, { 'order_id': adv['fou_wo_id'], 'sub_advance_id': adv['id'], 'sub_advance_date': adv['entry_date'], 'tot_advance_amt': adv['advance_amt'], 'balance_amt': adv['balance_amt'], 'current_adv_amt': 0.0, 'header_id': invoice_rec.id, }) return True def unlink(self, cr, uid, ids, context=None): unlink_ids = [] for rec in self.browse(cr, uid, ids): if rec.state != 'draft': raise osv.except_osv(_('Warning!'), _('You can not delete this entry !!')) else: unlink_ids.append(rec.id) return osv.osv.unlink(self, cr, uid, unlink_ids, context=context) def create(self, cr, uid, vals, context=None): return super(kg_fettling_invoice, self).create(cr, uid, vals, context=context) def write(self, cr, uid, ids, vals, context=None): vals.update({ 'update_date': time.strftime('%Y-%m-%d %H:%M:%S'), 'update_user_id': uid }) return super(kg_fettling_invoice, self).write(cr, uid, ids, vals, context) def send_to_dms(self, cr, uid, ids, context=None): rec = self.browse(cr, uid, ids[0]) res_rec = self.pool.get('res.users').browse(cr, uid, uid) rec_user = str(res_rec.login) rec_pwd = str(res_rec.password) rec_code = str(rec.name) encoded_user = base64.b64encode(rec_user) encoded_pwd = base64.b64encode(rec_pwd) url = 'http://192.168.1.7/sam-dms/login.html?xmxyypzr=' + encoded_user + '&mxxrqx=' + encoded_pwd + '&fettling_invoice=' + rec_code return { 'name': 'Go to website', 'res_model': 'ir.actions.act_url', 'type': 'ir.actions.act_url', 'target': 'current', 'url': url } _sql_constraints = [ ('name', 'unique(name)', 'No must be Unique !!'), ]
class ProfitLossReport(models.TransientModel): _name = 'profit.loss.report' categ_id = fields.Many2one( comodel_name='product.category', string='Product Category', readonly=True, ) categ_name = fields.Char( string='Brand', readonly=True, ) product_id = fields.Many2one( comodel_name='product.product', string='Referece', readonly=True, ) lot_id = fields.Many2one( comodel_name='stock.production.lot', string='Case No.', readonly=True, ) date_order = fields.Date( string='Date Registered', readonly=True, ) user_id = fields.Many2one( comodel_name='res.users', string='Salesperson', readonly=True, ) sale_order_id = fields.Many2one( comodel_name='sale.order', string='Quotation', readonly=True, ) invoice_id = fields.Many2one( comodel_name='account.invoice', string='Invoice', readonly=True, ) list_price = fields.Float( string='HK Retail', digits=dp.get_precision('Product Price'), readonly=True, ) discount = fields.Float( string="Discount (%)", digits=dp.get_precision('Discount'), compute='_get_discount', readonly=True, ) net_price = fields.Float( string="Net Price", digits=dp.get_precision('Product Price'), readonly=True, ) net_price_currency_id = fields.Many2one( comodel_name='res.currency', string='Net Price Currency', readonly=True, ) partner_id = fields.Many2one( comodel_name='res.partner', string='Customer', readonly=True, ) partner_ref = fields.Char( string='Customer Ref.', readonly=True, ) sale_order_note = fields.Text( string='Quotation Notes', readonly=True, ) # FIXME we may deprecate this field if not needed sale_state = fields.Selection( [('open', 'Open Payment'), ('balance', 'Balance Payment'), ('done', 'Done')], string='Sales Status', readonly=True, ) state = fields.Selection( [('purch_done', 'PO Done'), ('sale_done', 'SO Done'), ('sale_purch_done', 'SO and PO Done'), ('out_refund', 'Customer Refund'), ('in_refund', 'Supplier Refund')], string='Status', readonly=True, ) out_move_id = fields.Many2one( comodel_name='stock.move', string='Outgoing Move', readonly=True, ) out_move_date = fields.Date( string='Outgoing Move Date', readonly=True, ) in_move_id = fields.Many2one( comodel_name='stock.move', string='Incoming Move', readonly=True, ) in_move_date = fields.Date( string='Incoming Move Date', readonly=True, ) in_period_id = fields.Many2one( comodel_name='account.period', string='Period', readonly=True, ) in_move_quant_owner_id = fields.Many2one( comodel_name='res.partner', string='Owner', readonly=True, ) stock_type = fields.Selection( [('own', 'Own Stock'), ('vci', 'VCI')], string='Stock Type', readonly=True, ) purchase_order_id = fields.Many2one( comodel_name='purchase.order', string='Purchase Order', readonly=True, ) supplier_id = fields.Many2one( comodel_name='res.partner', string='Supplier', readonly=True, ) supplier_ref = fields.Char( string='Supplier Ref.', readonly=True, ) purchase_currency_id = fields.Many2one( comodel_name='res.currency', string='Purchase Currency', readonly=True, ) purchase_currency_price = fields.Float( string="Purchase Curr. Price", digits=dp.get_precision('Product Price'), readonly=True, ) exchange_rate = fields.Float( digits=(12, 6), string='FX Rate', readonly=True, ) purchase_base_price = fields.Float( string='Purchase Base Price', digits=dp.get_precision('Account'), readonly=True, ) purchase_invoice_id = fields.Many2one( comodel_name='account.invoice', string='Purchase Invoice', readonly=True, ) purchase_invoice_line_id = fields.Many2one( comodel_name='account.invoice.line', string='Purchase Invoice Line', readonly=True, ) supplier_invoice_number = fields.Char( string='Supplier Invoice No.', readonly=True, ) base_profit = fields.Float( string='Base Profit', readonly=True, ) base_profit_percent = fields.Float( string='Profit %', digits=dp.get_precision('Account'), readonly=True, ) customer_payment_ids = fields.Many2many( string='Customer Payment', comodel_name='account.move.line', related='invoice_id.payment_ids', readonly=True, ) supplier_payment_ids = fields.Many2many( string='Supplier Payment', comodel_name='account.move.line', related='purchase_invoice_id.payment_ids', readonly=True, ) supplier_payment_dates = fields.Char( string='Payment Date', readonly=True, ) supplier_payment_ref = fields.Char( string='Payment Ref.', readonly=True, ) supplier_payment_state = fields.Selection( [('to_pay', 'To Be Paid'), ('done', 'Done')], string='Supplier Payment Status', readonly=True, ) customer_invoice_type = fields.Selection( [('out_invoice', 'Customer Invoice'), ('out_refund', 'Customer Refund')], readonly=True, ) supplier_invoice_type = fields.Selection( [('in_invoice', 'Supplier Invoice'), ('in_refund', 'Supplier Refund')], readonly=True, ) image_small = fields.Binary( 'Image', related='product_id.product_tmpl_id.image_small', readonly=True, ) customer_payment_information = fields.Char( string="Payment Information", readonly=True, ) base_amount = fields.Float( string="Base Amount", digits=dp.get_precision('Product Price'), readonly=True, ) @api.multi def _get_discount(self): for rec in self: if not rec.list_price or not rec.net_price: rec.discount = 0.0 else: rec.discount = (1 - rec.net_price / rec.list_price) * 100 return
class AccountInvoice(models.Model): _inherit = 'account.invoice' amount_retention = fields.Float( string='Retention', digits=dp.get_precision('Account'), readonly=True, states={'draft': [('readonly', False)]}, ) retention_on_payment = fields.Boolean( string='Retention on Payment', compute='_compute_retention_on_payment', store=True, help="If checked, retention will done during payment", ) move_ids = fields.One2many( 'account.move.line', related='move_id.line_id', string='Journal Items', readonly=True, ) date_paid = fields.Date( string='Paid Date', compute='_compute_date_paid', store=True, ) @api.multi @api.depends('state') def _compute_date_paid(self): for rec in self: if rec.state == 'paid' and rec.payment_ids: rec.date_paid = max(rec.payment_ids.mapped('date')) elif rec.state == 'open': rec.date_paid = False @api.multi @api.depends('invoice_line.price_subtotal', 'tax_line.amount', 'amount_retention') def _compute_amount(self): super(AccountInvoice, self)._compute_amount() for rec in self: rec.amount_tax = sum(line.amount for line in rec.tax_line) amount_total = rec.amount_untaxed + rec.amount_tax if not rec.retention_on_payment: rec.amount_total = amount_total - rec.amount_retention # RET else: rec.amount_total = amount_total @api.multi @api.depends('partner_id') def _compute_retention_on_payment(self): for rec in self: rec.retention_on_payment = \ self.env.user.company_id.retention_on_payment @api.multi def invoice_pay_customer(self): res = super(AccountInvoice, self).invoice_pay_customer() if res: res['context']['default_amount'] = 0.0 return res #////////////////////////////////////////////////////////////////////////////////////////////////// @api.constrains('date_due','date_invoice') def date_compare(self): if self.type == 'in_invoice' and self.date_due != False: if self.date_due < self.date_invoice: raise ValidationError('Due Date is greater than or equal to Posting date')
# You should have received a copy of the GNU Affero General Public License # # along with this program. If not, see <http://www.gnu.org/licenses/>. # ############################################################################### import time from openerp.osv import orm, fields from openerp.addons import decimal_precision as dp from .res_company import COMPANY_FISCAL_TYPE, COMPANY_FISCAL_TYPE_DEFAULT FISCAL_RULE_COLUMNS = { "partner_fiscal_type_id": fields.many2one("l10n_br_account.partner.fiscal.type", "Tipo Fiscal do Parceiro"), "fiscal_category_id": fields.many2one("l10n_br_account.fiscal.category", "Categoria"), "fiscal_type": fields.selection(COMPANY_FISCAL_TYPE, u"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" ), } OTHERS_FISCAL_RULE_COLUMNS_TEMPLATE = { "parent_id": fields.many2one("account.fiscal.position.rule.template", "Regra Pai"), "child_ids": fields.one2many("account.fiscal.position.rule.template", "parent_id", "Regras Filhas"), } OTHERS_FISCAL_RULE_COLUMNS = { "parent_id": fields.many2one("account.fiscal.position.rule", "Regra Pai"), "child_ids": fields.one2many("account.fiscal.position.rule", "parent_id", "Regras Filhas"), }
class account_move_line(osv.osv): _inherit = "account.move.line" def _curr_debit_credit(self, cr, uid, ids, field_names, args, context=None): res = {} if context is None: context = {} currency_obj = self.pool.get('res.currency') alt_curr_ids = currency_obj.search(cr, uid, [('alt_currency','=',True)]) alt_curr_id = alt_curr_ids and alt_curr_ids[0] or False ctx = context.copy() for move_line in self.browse(cr, uid, ids, context=context): res[move_line.id] = { 'curr_debit': 0.0, 'curr_credit': 0.0, } if alt_curr_id: ml_currency_id = move_line.currency_id and move_line.currency_id.id or False if ml_currency_id==alt_curr_id: res[move_line.id].update({ 'curr_debit': move_line.debit > 0 and abs(move_line.amount_currency) or 0.0, 'curr_credit': move_line.credit > 0 and abs(move_line.amount_currency) or 0.0, }) else: ctx['date'] = move_line.date res[move_line.id].update({ 'curr_debit': currency_obj.compute(cr, uid, move_line.company_id.currency_id.id, alt_curr_id, move_line.debit, round=False, context=ctx), 'curr_credit': currency_obj.compute(cr, uid, move_line.company_id.currency_id.id, alt_curr_id, move_line.credit, round=False, context=ctx), }) return res @api.one @api.depends('currency_id') def _transaction_currency(self): if self.currency_id: self.transaction_curr_id = self.currency_id.id else: self.transaction_curr_id = self.company_id.currency_id.id _columns = { 'curr_debit': fields.function(_curr_debit_credit, type="float", digits_compute=dp.get_precision('Account'), string='Debit %s', multi='debit_credit', help="Debit in Secundary Currency", store = { 'account.move.line': (lambda self, cr, uid, ids, c=None: ids, ['debit', 'currency_id', 'amount_currency'], 20), 'res.currency': (lambda self, cr, uid, ids, c=None: self.pool['account.move.line'].search(cr, uid, []), ['alt_currency'], 20), }), 'curr_credit': fields.function(_curr_debit_credit, type="float", digits_compute=dp.get_precision('Account'), string='Credit %s', multi='debit_credit', help="Credit in Secundary Currency", store = { 'account.move.line': (lambda self, cr, uid, ids, c=None: ids, ['credit', 'currency_id', 'amount_currency'], 20), 'res.currency': (lambda self, cr, uid, ids, c=None: self.pool['account.move.line'].search(cr, uid, []), ['alt_currency'], 20), }), 'transaction_curr_id': fields.many2one('res.currency', 'Transaction currency', compute="_transaction_currency", readonly=True, help="Currency of this transaction."), } def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): if context is None: context = {} res = super(account_move_line, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu) currency_obj = self.pool.get('res.currency') alt_curr_ids = currency_obj.search(cr, uid, [('alt_currency','=',True)]) alt_curr_id = alt_curr_ids and alt_curr_ids[0] or False if alt_curr_id: alt_curr_name = currency_obj.read(cr, uid, alt_curr_id, ['name'], context=context)['name'] precision = self.pool.get('decimal.precision').precision_get(cr, uid, 'Account') for field in res['fields']: if alt_curr_id and field.startswith('curr_'): res['fields'][field]['string'] = res['fields'][field]['string'].replace('%s', alt_curr_name) if field == 'initial_bal': res['fields'][field]['digits'] = (16, precision) if field == 'curr_initial_bal': res['fields'][field]['digits'] = (16, precision) if not context.get('show_curr_debit_credit', False): res['fields'].pop('curr_debit', None) res['fields'].pop('curr_credit', None) doc = etree.XML(res['arch']) for node in doc.xpath("//field[@name='curr_debit']"): node.getparent().remove(node) for node in doc.xpath("//field[@name='curr_credit']"): node.getparent().remove(node) res['arch'] = etree.tostring(doc) return res def open_account_move(self, cr, uid, ids, context=None): context = context or {} row = self.browse(cr, uid, ids, context=context)[0] return { 'type' : 'ir.actions.act_window', 'name' : _('Journal Entry'), 'res_model': 'account.move', 'view_type': 'form', 'view_mode': 'form', 'target': 'current', 'res_id': row.move_id.id, 'context': str(context) } def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False, lazy=True): context = context or {} res = super(account_move_line, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby, lazy=lazy) if context.get('include_initial_bal', False) and ('initial_bal' in fields or 'curr_initial_bal' in fields): precision = self.pool.get('decimal.precision').precision_get(cr, uid, 'Account') for line in res: lines = self.search(cr, uid, line.get('__domain', domain), context=context) lines_rec = self.browse(cr, uid, lines, context=context) if 'initial_bal' in fields: line['initial_bal'] = sum(line_rec.initial_bal for line_rec in lines_rec) line['initial_bal'] = float_round(line['initial_bal'], precision_digits=precision) if 'curr_initial_bal' in fields: line['curr_initial_bal'] = sum(line_rec.curr_initial_bal for line_rec in lines_rec) line['curr_initial_bal'] = float_round(line['curr_initial_bal'], precision_digits=precision) return res
def _onchange_partner(self): if self.partner_id: self.delivery_id = self.partner_id # MAJ d'un autre champ # M2M : 2 possibilités : # - liste d'IDs, mais ça va AJOUTER les ids, comme (4, [IDs]) # - [(6, 0, [IDs])], ce qui va remplacer les ids # (cf module product_category_tax dans akretion/odoo-usability) # On utilise un autre M2M pour le mettre à jour, on peut faire # self.champ_M2M_ids.ids -> ça donne la liste des IDs # M2O : recordset (ou ID) # O2M : ?? # là, Odoo va jouer automatiquement le @api.onchange du champ delivery_id # pas besoin d'appeler le onchange de delivery_id dans notre code # Here, all form values are set on self # assigned values are not written to DB, but returned to the client # It is not possible to output a warning # It is not possible to put a raise UserError() # in this function (it will crash odoo) res = {'warning': {'title': _('Be careful'), {'message': _('here is the msg')}} # pour un domaine res = {'domain': { 'champ1': "[('product_id', '=', product_id)]", 'champ2': "[]"}, } return res # si je n'ai ni warning ni domain, je n'ai pas besoin de faire un return # Fonction on_change déclarée dans la vue form/tree @api.multi def product_id_change(self, cr, uid, ids, champ1, champ2, context): # ATTENTION : a priori, on ne doit pas utiliser ids dans le code de la # fonction, car quand on fait un on_change avant le save, ids = [] # Dans la vue XML : # <field name="product_id" # on_change="product_id_change(champ1, champ2, context)" /> # Piège : quand un champ float est passé dans un on_change, # si la personne avait tapé un entier, il va être passé en argument en # tant que integer et non en tant que float! raise orm.except_orm() # => il ne remet PAS l'ancienne valeur qui a déclanché le on_change # Pour mettre à jour des valeurs : return {'value': {'champ1': updated_value1, 'champ2': updated_value2}} # => à savoir : les onchange de 'champ1' et 'champ2' sont joués à # leur tour car leur valeur a été changée # si ces nouveaux on_change changent le product_id, # le product_id_change ne sera pas rejoué # Pour mettre un domaine : return {'domain': { 'champ1': "[('product_id', '=', product_id)]", 'champ2': "[]"}, } # l'intégralité du domaine est dans une string # Pour retourner un message de warning : return {'warning': { 'title': _('Le titre du msg de warn'), 'message': _("Ce que j'ai à te dire %s") % (text)}} # Pour ne rien faire return False # return True, ça marche en 7.0 mais ça bug en 6.1 # La fonction de calcul du champ function price_subtotal @api.one # auto-loop decorator @api.depends('price_unit', 'discount', 'invoice_line_tax_id', 'quantity', 'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id') # @api.depends est utilisé pour: invalidation cache, recalcul, onchange # donc, maintenant, le fait d'avoir un champ calculé fait qu'il est # automatiquement mis à jour dans la vue quand un de ses champs 'depends' # est modifié ! COOOOOL ! # ATTENTION : si chgt de @api.depends, faire -u module ! # Pour un one2many : ne PAS juste indiquer le nom du champ o2m, sinon il ne fait rien # il faut aussi indiquer un champ sur le O2M. Exemple : 'line_ids.request_id' # Apparemment, on peut mettre dans @api.depends un champ fonction stocké et ça va bien # faire le recalcul en cascade # (ça n'a pas l'air de marcher qd on met un api.depends sur un champ non stocké) def _compute_price(self): price = self.price_unit * (1 - (self.discount or 0.0) / 100.0) taxes = self.invoice_line_tax_id.compute_all(price, self.quantity, product=self.product_id, partner=self.invoice_id.partner_id) self.price_subtotal = taxes['total'] # calcul et stockage de la valeur self.second_field = 'iuit' # calcul et stockage d'un 2e champ # equivalent de multi='pouet' # Pour un champ O2M, envoyer un recordset multiple ou une liste d'IDS # pour un champ M2O, donner le recordset ou l'ID if self.invoice_id: self.price_subtotal = self.invoice_id.currency_id.round(self.price_subtotal) # Pas besoin de return ! # on ne peut PAS faire un self.write({}) dans la fonction de calcul d'un champ fonction # Pour un champ fonction, on peut aussi faire @api.multi: # untaxed = fields.Float(compute='_amounts') # taxes = fields.Float(compute='_amounts') # total = fields.Float(compute='_amounts') @api.multi @api.depends('lines.amount', 'lines.taxes') def _amounts(self): for order in self: order.untaxed = sum(line.amount for line in order.lines) order.taxes = sum(line.taxes for line in order.lines) order.total = order.untaxed + order + taxes # Champ fonction inverse='_inverse_price' @api.one def _inverse_loud(self): self.name = (self.loud or '').lower() # MAJ du ou des autres champs # Champ fonction search='_search_price' def _search_loud(self, operator, value): if value is not False: value = value.lower() today = fields.Date.context_today(self) self._cr.execute('SELECT id FROM [cur_obj] WHERE (fortress_type <> %s OR (fortress_type = %s AND effectivity_date is not null)) AND (end_date is null OR end_date > %s)', (today, )) res_ids = [x[0] for x in self._cr.fetchall()] res = [('id', 'in', res_ids)] # recherche sur les autres champs return res # Fonction default=_default_account @api.model def _default_account(self): return valeur_par_defaut # M2O : retourne un recordset (ne PAS retourner False !) # O2M : retourne une liste de dict contenant la valeur des champs # date : string ou objet datetime # Fonction pour fields.selection @api.model def _type_list_get(self): return [('key1', _('String1')), ('key2', _('String2'))] ### CHAMPS # id, create_uid, write_uid, create_date et write_date # sont déjà utilisable dans le code python sans re-définition active = fields.Boolean(default=True) # Par défaut, string = nom du champ avec majuscule pour chaque début de mot login = fields.Char( string='Login', size=16, translate=True, required=True, help="My help message") display_name = fields.Char( string='Display Name', compute='_compute_display_name', readonly=True, store=True) comment = fields.Text(string='Comment', translate=True) html = fields.Html(string='report', translate=True) code_digits = fields.Integer( string='# of Digits', track_visibility='always', default=12, groups='base.group_user') # OU groups=['base.group_user', 'base.group_hr_manager'] # groups = XMLID : restriction du read/write et invisible ds les vues sequence = fields.Integer(default=10) # track_visibility = always ou onchange amount_untaxed = fields.Float( 'Amount untaxed', digits=dp.get_precision('Account')) # digits=(precision, scale) # Scale est le nombre de chiffres après la virgule # quand le float est un fields.float ou un fields.function, # on met l'option : digits=dp.get_precision('Account') # Autres valeurs possibles pour get_precision : product/product_data.xml # Product Price, Discount, Stock Weight, Product Unit of Measure, # Product UoS # fields.Monetary is only in version >= 9.0 debit = fields.Monetary(default=0.0, currency_field='company_currency_id') start_date = fields.Date( string='Start Date', copy=False, default=fields.Date.context_today) # similaire : fields.Datetime and fields.Time type = fields.Selection([ ('import', 'Import'), ('export', 'Export'), ], string="Type", default=lambda self: self._context.get('type', 'export')) # FIELDS.SELECTION ac selection dynamique : # type = fields.Selection('_type_list_get', string='Type', help='Pouet'), # Plus besoin de la double fonction pour que la 2e soit héritable # Pour ajouter des champs à un fields.Selection existant: # fields.Selection( # selection_add=[('new_key1', 'My new key1'), ('new_key2', 'My New Key2')]) picture = fields.Binary(string='Picture') # Pour fields.binary, il existe une option filters='*.png, *.gif', # qui restreint les formats de fichiers sélectionnables dans # la boite de dialogue, mais ça ne marche pas en GTK (on # ne peut rien sélectionner) et c'est pas supporté en Web, cf # https://bugs.launchpad.net/openobject-server/+bug/1076895 picture_filename = fields.Char(string='Filename') # Les champs "picture" et "picture_filename" sont liés ensemble dans la vue # via la balise filename="picture_filename" sur le champ 'picture' # Il faut que le champ 'picture_filename' soit présent dans la vue # (il peut être invisible) # Pour un fichier à télécharger d'Odoo, le nom du fichier aura la valeur de # picture_filename # Pour un fichier à uploader dans Odoo, 'picture_filename' vaudra le nom # du fichier uploadé par l'utilisateur # Exemple de champ fonction stocké price_subtotal = fields.Float( string='Amount', digits= dp.get_precision('Account'), store=True, readonly=True, compute='_compute_price') # Exemple de champ function non stocké loud = fields.Char( store=False, compute='_compute_loud', inverse='_inverse_loud', search='_search_loud') account_id = fields.Many2one('account.account', string='Account', required=True, domain=[('type', 'not in', ['view', 'closed'])], default=_default_account) company_id = fields.Many2one( 'res.company', string='Company', ondelete='cascade', required=True, default=lambda self: self.env['res.company']._company_default_get( 'product.code')) # si on veut que tous les args soient nommés : comodel_name='res.company' user_id = fields.Many2one( 'res.users', string='Salesman', default=lambda self: self.env.user) # ATTENTION : si j'ai déjà un domaine sur la vue, # c'est le domaine sur la vue qui prime ! # ondelete='cascade' : # le fait de supprimer la company va supprimer l'objet courant ! # ondelete='set null' (default) # si on supprime la company, le champ company_id est mis à 0 # ondelete='restrict' : # si on supprime la company, ça déclanche une erreur d'intégrité ! # Champ Relation company_currency_id = fields.Many2one( 'res.currency', string='Currency', related='company_id.currency_id', store=True) # ATTENTION, en nouvelle API, on ne peut PAS faire un fields.Char qui # soit un related d'un fields.Selection (bloque le démarrage d'Odoo # sans message d'erreur !) line_ids = fields.One2many( 'product.code.line', 'parent_id', string='Product lines', states={'done': [('readonly', True)]}, copy=True) # OU comodel_name='product.code.line', inverse_name='parent_id' # 2e arg = nom du champ sur l'objet destination qui est le M20 inverse # en v8 : # copy=True pour que les lignes soient copiées lors d'un duplicate # sinon, mettre copy=False (ça ne peut être qu'un booléen) # Valeur par défaut du paramètre "copy": True for normal fields, False for # one2many and computed fields, including property fields and related fields # ATTENTION : pour que states={} marche sur le champ A et que le # champ A est dans la vue tree, alors il faut que le champ "state" # soit aussi dans la vue tree. partner_ids = fields.Many2many( 'res.partner', 'product_code_partner_rel', 'code_id', 'partner_id', 'Related Partners') # 2e arg = nom de la table relation # 3e arg ou id1 = nom de la colonne dans la table relation # pour stocker l'ID du product.code # 4e arg ou id2 = nom de la colonne dans la table relation # pour stocker l'ID du res.partner # OU partner_ids = fields.Many2many( 'res.partner', column1='code_id', column2='partner_id', string='Related Partners') # OU partner_ids = fields.Many2many( 'res.partner', string='Related Partners') # Pour les 2 dernières définitions du M2M, il ne faut pas avoir # plusieurs champs M2M qui pointent du même obj vers le même obj # Champ property: il suffit de définit le champ comme un champ normal # et d'ajouter un argument company_dependent=True # Quand on veut lire la valeur d'un champ property dans une société # qui n'est pas celle de l'utilisateur, il faut passer dans le context # 'force_company': 8 (8 = ID de la company) }
class LunchOrderLine(models.Model): _name = 'lunch.order.line' _description = 'lunch order line' name = fields.Char(related='product_id.name', readonly=True) order_id = fields.Many2one('lunch.order', 'Order', ondelete='cascade', required=True) product_id = fields.Many2one('lunch.product', 'Product', required=True) category_id = fields.Many2one('lunch.product.category', string='Product Category', related='product_id.category_id', readonly=True, store=True) date = fields.Date(string='Date', related='order_id.date', readonly=True, store=True) supplier = fields.Many2one('res.partner', string='Vendor', related='product_id.supplier', readonly=True, store=True) user_id = fields.Many2one('res.users', string='User', related='order_id.user_id', readonly=True, store=True) note = fields.Text('Note') price = fields.Float(related='product_id.price', readonly=True, store=True, digits=dp.get_precision('Account')) state = fields.Selection([('new', 'New'), ('confirmed', 'Received'), ('ordered', 'Ordered'), ('cancelled', 'Cancelled')], 'Status', readonly=True, index=True, default='new') cashmove = fields.One2many('lunch.cashmove', 'order_id', 'Cash Move') currency_id = fields.Many2one('res.currency', related='order_id.currency_id') @api.one def order(self): """ The order_line is ordered to the vendor but isn't received yet """ self.state = 'ordered' @api.one def confirm(self): """ confirm one or more order line, update order status and create new cashmove """ if self.state != 'confirmed': values = { 'user_id': self.user_id.id, 'amount': -self.price, 'description': self.product_id.name, 'order_id': self.id, 'state': 'order', 'date': self.date, } self.env['lunch.cashmove'].create(values) self.state = 'confirmed' @api.one def cancel(self): """ cancel one or more order.line, update order status and unlink existing cashmoves """ self.state = 'cancelled' self.cashmove.unlink()
cr, uid, line.taxes_id, line.price, line.qty, line.product_id, line.back_order_id.partner_id ) cur = line.back_order_id.pricelist_id.currency_id res[line.id] = cur_obj.round(cr, uid, cur, taxes["total"]) return res def _get_uom_id(self, cr, uid, context=None): try: proxy = self.pool.get("ir.model.data") result = proxy.get_object_reference(cr, uid, "product", "product_uom_unit") return result[1] except Exception, ex: return False _columns = { # 'location_destination_id': fields.many2one('stock.location', 'Stock Destination Location'), # 'location_id': fields.many2one('stock.location', 'Stock Source Location'), "product_id": fields.many2one("product.product", "Product"), "back_order_id": fields.many2one("back.to.back.order", "Back Order"), "qty": fields.float("Quantity"), "price": fields.float("Unit Price"), "subtotal": fields.function(_amount_line, string="Subtotal", digits_compute=dp.get_precision("Account")), "taxes_id": fields.many2many("account.tax", "purchase_order_taxe", "ord_id", "tax_id", "Taxes"), "product_uom": fields.many2one("product.uom", "Product Unit of Measure", required=True), } _defaults = {"product_uom": _get_uom_id} # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
class MailTrackingEmail(models.Model): _name = "mail.tracking.email" _order = 'time desc' _rec_name = 'display_name' _description = 'MailTracking email' name = fields.Char(string="Subject", readonly=True, index=True) display_name = fields.Char(string="Display name", readonly=True, store=True, compute="_compute_display_name") timestamp = fields.Float(string='UTC timestamp', readonly=True, digits=dp.get_precision('MailTracking Timestamp')) time = fields.Datetime(string="Time", readonly=True) date = fields.Date(string="Date", readonly=True, compute="_compute_date", store=True) mail_message_id = fields.Many2one(string="Message", comodel_name='mail.message', readonly=True) mail_id = fields.Many2one(string="Email", comodel_name='mail.mail', readonly=True) partner_id = fields.Many2one(string="Partner", comodel_name='res.partner', readonly=True) recipient = fields.Char(string='Recipient email', readonly=True) recipient_address = fields.Char(string='Recipient email address', readonly=True, store=True, compute='_compute_recipient_address') sender = fields.Char(string='Sender email', readonly=True) state = fields.Selection( [ ('error', 'Error'), ('deferred', 'Deferred'), ('sent', 'Sent'), ('delivered', 'Delivered'), ('opened', 'Opened'), ('rejected', 'Rejected'), ('spam', 'Spam'), ('unsub', 'Unsubscribed'), ('bounced', 'Bounced'), ('soft-bounced', 'Soft bounced'), ], string='State', index=True, readonly=True, default=False, help=" * The 'Error' status indicates that there was an error " "when trying to sent the email, for example, " "'No valid recipient'\n" " * The 'Sent' status indicates that message was succesfully " "sent via outgoing email server (SMTP).\n" " * The 'Delivered' status indicates that message was " "succesfully delivered to recipient Mail Exchange (MX) server.\n" " * The 'Opened' status indicates that message was opened or " "clicked by recipient.\n" " * The 'Rejected' status indicates that recipient email " "address is blacklisted by outgoing email server (SMTP). " "It is recomended to delete this email address.\n" " * The 'Spam' status indicates that outgoing email " "server (SMTP) consider this message as spam.\n" " * The 'Unsubscribed' status indicates that recipient has " "requested to be unsubscribed from this message.\n" " * The 'Bounced' status indicates that message was bounced " "by recipient Mail Exchange (MX) server.\n" " * The 'Soft bounced' status indicates that message was soft " "bounced by recipient Mail Exchange (MX) server.\n") error_smtp_server = fields.Char(string='Error SMTP server', readonly=True) error_type = fields.Char(string='Error type', readonly=True) error_description = fields.Char(string='Error description', readonly=True) bounce_type = fields.Char(string='Bounce type', readonly=True) bounce_description = fields.Char(string='Bounce description', readonly=True) tracking_event_ids = fields.One2many(string="Tracking events", comodel_name='mail.tracking.event', inverse_name='tracking_email_id', readonly=True) @api.model def tracking_ids_recalculate(self, model, email_field, tracking_field, email, new_tracking=None): objects = self.env[model].search([ (email_field, '=ilike', email), ]) for obj in objects: trackings = obj[tracking_field] if new_tracking: trackings |= new_tracking trackings = trackings._email_score_tracking_filter() if set(obj[tracking_field].ids) != set(trackings.ids): if trackings: obj.write({tracking_field: [(6, False, trackings.ids)]}) else: obj.write({tracking_field: [(5, False, False)]}) return objects @api.model def _tracking_ids_to_write(self, email): trackings = self.env['mail.tracking.email'].search([ ('recipient_address', '=ilike', email) ]) trackings = trackings._email_score_tracking_filter() if trackings: return [(6, False, trackings.ids)] else: return [(5, False, False)] @api.multi def _email_score_tracking_filter(self): """Default email score filter for tracking emails""" # Consider only last 10 tracking emails return self.sorted(key=lambda r: r.time, reverse=True)[:10] @api.model def email_score_from_email(self, email): trackings = self.env['mail.tracking.email'].search([ ('recipient_address', '=ilike', email) ]) return trackings.email_score() @api.multi def email_score(self): """Default email score algorimth""" score = 50.0 trackings = self._email_score_tracking_filter() for tracking in trackings: if tracking.state in ('error', ): score -= 50.0 elif tracking.state in ('rejected', 'spam', 'bounced'): score -= 25.0 elif tracking.state in ('soft-bounced', 'unsub'): score -= 10.0 elif tracking.state in ('delivered', ): score += 5.0 elif tracking.state in ('opened', ): score += 10.0 if score > 100.0: score = 100.0 return score @api.multi @api.depends('recipient') def _compute_recipient_address(self): for email in self: matches = re.search(r'<(.*@.*)>', email.recipient) if matches: email.recipient_address = matches.group(1) else: email.recipient_address = email.recipient @api.multi @api.depends('name', 'recipient') def _compute_display_name(self): for email in self: parts = [email.name] if email.recipient: parts.append(email.recipient) email.display_name = ' - '.join(parts) @api.multi @api.depends('time') def _compute_date(self): for email in self: email.date = fields.Date.to_string( fields.Date.from_string(email.time)) @api.model def create(self, vals): tracking = super(MailTrackingEmail, self).create(vals) self.tracking_ids_recalculate('res.partner', 'email', 'tracking_email_ids', tracking.recipient_address, new_tracking=tracking) return tracking def _get_mail_tracking_img(self): base_url = self.env['ir.config_parameter'].get_param('web.base.url') path_url = ( 'mail/tracking/open/%(db)s/%(tracking_email_id)s/blank.gif' % { 'db': self.env.cr.dbname, 'tracking_email_id': self.id, }) track_url = urlparse.urljoin(base_url, path_url) return ('<img src="%(url)s" alt="" ' 'data-odoo-tracking-email="%(tracking_email_id)s"/>' % { 'url': track_url, 'tracking_email_id': self.id, }) @api.multi def smtp_error(self, mail_server, smtp_server, exception): self.sudo().write({ 'error_smtp_server': tools.ustr(smtp_server), 'error_type': exception.__class__.__name__, 'error_description': tools.ustr(exception), 'state': 'error', }) return True @api.multi def tracking_img_add(self, email): self.ensure_one() tracking_url = self._get_mail_tracking_img() if tracking_url: body = tools.append_content_to_html(email.get('body', ''), tracking_url, plaintext=False, container_tag='div') email['body'] = body return email def _message_partners_check(self, message, message_id): mail_message = self.mail_message_id partners = mail_message.notified_partner_ids | mail_message.partner_ids if (self.partner_id and self.partner_id not in partners): # If mail_message haven't tracking partner, then # add it in order to see his trackking status in chatter if mail_message.subtype_id: mail_message.sudo().write({ 'notified_partner_ids': [(4, self.partner_id.id)], }) else: mail_message.sudo().write({ 'partner_ids': [(4, self.partner_id.id)], }) return True @api.multi def _tracking_sent_prepare(self, mail_server, smtp_server, message, message_id): self.ensure_one() ts = time.time() dt = datetime.utcfromtimestamp(ts) self._message_partners_check(message, message_id) self.sudo().write({'state': 'sent'}) return { 'recipient': message['To'], 'timestamp': '%.6f' % ts, 'time': fields.Datetime.to_string(dt), 'tracking_email_id': self.id, 'event_type': 'sent', 'smtp_server': smtp_server, } def _event_prepare(self, event_type, metadata): self.ensure_one() m_event = self.env['mail.tracking.event'] method = getattr(m_event, 'process_' + event_type, None) if method and hasattr(method, '__call__'): return method(self, metadata) else: # pragma: no cover _logger.info('Unknown event type: %s' % event_type) return False def _concurrent_events(self, event_type, metadata): m_event = self.env['mail.tracking.event'] self.ensure_one() concurrent_event_ids = False if event_type in {'open', 'click'}: ts = metadata.get('timestamp', time.time()) delta = EVENT_OPEN_DELTA if event_type == 'open' \ else EVENT_CLICK_DELTA domain = [ ('timestamp', '>=', ts - delta), ('timestamp', '<=', ts + delta), ('tracking_email_id', '=', self.id), ('event_type', '=', event_type), ] if event_type == 'click': domain.append(('url', '=', metadata.get('url', False))) concurrent_event_ids = m_event.search(domain) return concurrent_event_ids @api.multi def event_create(self, event_type, metadata): event_ids = self.env['mail.tracking.event'] for tracking_email in self: other_ids = tracking_email._concurrent_events(event_type, metadata) if not other_ids: vals = tracking_email._event_prepare(event_type, metadata) if vals: event_ids += event_ids.sudo().create(vals) partners = self.tracking_ids_recalculate( 'res.partner', 'email', 'tracking_email_ids', tracking_email.recipient_address) if partners: partners.email_score_calculate() else: _logger.debug("Concurrent event '%s' discarded", event_type) return event_ids @api.model def event_process(self, request, post, metadata, event_type=None): # Generic event process hook, inherit it and # - return 'OK' if processed # - return 'NONE' if this request is not for you # - return 'ERROR' if any error return 'NONE' # pragma: no cover
class ProcurementOrderQuantity(models.Model): _inherit = 'procurement.order' move_dest_id = fields.Many2one('stock.move', index=True) product_id = fields.Many2one('product.product', index=True) state = fields.Selection(index=True) qty = fields.Float( string="Quantity", digits_compute=dp.get_precision('Product Unit of Measure'), help='Quantity in the default UoM of the product', compute="_compute_qty", store=True) @api.multi @api.depends('product_qty', 'product_uom') def _compute_qty(self): for m in self: qty = self.env['product.uom']._compute_qty_obj( m.product_uom, m.product_qty, m.product_id.uom_id) m.qty = qty @api.model def _procure_orderpoint_confirm(self, use_new_cursor=False, company_id=False, run_procurements=True, run_moves=True): """ Create procurement based on orderpoint :param bool use_new_cursor: if set, use a dedicated cursor and auto-commit after processing each procurement. This is appropriate for batch jobs only. """ orderpoint_env = self.env['stock.warehouse.orderpoint'] dom = company_id and [('company_id', '=', company_id)] or [] if self.env.context.get( 'compute_product_ids' ) and not self.env.context.get('compute_all_products'): dom += [('product_id', 'in', self.env.context.get('compute_product_ids'))] if self.env.context.get( 'compute_supplier_ids' ) and not self.env.context.get('compute_all_products'): supplierinfo_ids = self.env['product.supplierinfo']. \ search([('name', 'in', self.env.context['compute_supplier_ids'])]) read_supplierinfos = supplierinfo_ids.read( ['id', 'product_tmpl_id'], load=False) dom += [('product_id.product_tmpl_id', 'in', [item['product_tmpl_id'] for item in read_supplierinfos])] orderpoints = orderpoint_env.search(dom) if run_procurements: self.env['procurement.order'].run_confirm_procurements( company_id=company_id) if run_moves: domain = company_id and [('company_id', '=', company_id)] or False self.env['procurement.order'].run_confirm_moves(domain) last_date_done = dt.now() - relativedelta(months=1) last_date_done = fields.Datetime.to_string(last_date_done) self.env.cr.execute( """DELETE FROM stock_scheduler_controller WHERE done IS TRUE AND date_done < %s""", (last_date_done, )) self.env.cr.execute( """INSERT INTO stock_scheduler_controller (orderpoint_id, product_id, location_id, location_sequence, route_sequence, run_procs, done, create_date, write_date, create_uid, write_uid) WITH user_id AS (SELECT %s AS user_id), orderpoints_to_insert AS ( SELECT op.id AS orderpoint_id, op.product_id, op.location_id, COALESCE(sl.stock_scheduler_sequence, 0) AS location_sequence, COALESCE(slr.stock_scheduler_sequence, 0) AS route_sequence, FALSE AS run_procs, FALSE AS done, CURRENT_TIMESTAMP AS create_date, CURRENT_TIMESTAMP AS write_date, (SELECT user_id FROM user_id) AS create_uid, (SELECT user_id FROM user_id) AS write_uid FROM stock_warehouse_orderpoint op LEFT JOIN stock_location sl ON sl.id = op.location_id LEFT JOIN product_product pp ON pp.id = op.product_id LEFT JOIN stock_route_product rel ON rel.product_id = pp.product_tmpl_id LEFT JOIN stock_location_route slr ON slr.id = rel.route_id WHERE op.id IN %s GROUP BY op.id, sl.stock_scheduler_sequence, slr.stock_scheduler_sequence), list_sequences AS ( SELECT location_sequence, route_sequence FROM orderpoints_to_insert GROUP BY location_sequence, route_sequence) SELECT * FROM orderpoints_to_insert UNION ALL SELECT NULL AS orderpoint_id, NULL AS product_id, NULL AS location_id, location_sequence, route_sequence, TRUE AS run_procs, FALSE AS done, CURRENT_TIMESTAMP AS create_date, CURRENT_TIMESTAMP AS write_date, (SELECT user_id FROM user_id) AS create_uid, (SELECT user_id FROM user_id) AS write_uid FROM list_sequences""", (self.env.uid, tuple(orderpoints.ids + [0]))) return {} @api.multi def cancel(self): result = super(ProcurementOrderQuantity, self).cancel() if self.env.context.get('unlink_all_chain'): delete_moves_cancelled_by_planned = bool( self.env['ir.config_parameter'].get_param( 'stock_procurement_just_in_time.delete_moves_cancelled_by_planned', default=False)) if delete_moves_cancelled_by_planned: moves_to_unlink = self.env['stock.move'] procurements_to_unlink = self.env['procurement.order'] for rec in self: parent_moves = self.env['stock.move'].search([ ('procurement_id', '=', rec.id) ]) for move in parent_moves: if move.state == 'cancel': moves_to_unlink += move if procurements_to_unlink not in procurements_to_unlink and move.procurement_id and \ move.procurement_id.state == 'cancel' and \ not any([move.state == 'done' for move in move.procurement_id.move_ids]): procurements_to_unlink += move.procurement_id if moves_to_unlink: moves_to_unlink.unlink() if procurements_to_unlink: procurements_to_unlink.unlink() return result @api.model def propagate_cancel(self, procurement): """ Improves the original propagate_cancel, in order to cancel it even if one of its moves is done. """ ignore_move_ids = procurement.rule_id.action == 'move' and procurement.move_ids and \ procurement.move_ids.filtered(lambda move: move.state == 'done').ids or [] if procurement.rule_id.action == 'move': # Keep proc with new qty if some moves are already done procurement.remove_done_moves() return super( ProcurementOrderQuantity, self.sudo().with_context( ignore_move_ids=ignore_move_ids)).propagate_cancel(procurement) @api.model def remove_done_moves(self): """Splits the given procs creating a copy with the qty of their done moves and set to done. """ for procurement in self: if procurement.rule_id.action == 'move': qty_done = sum([ move.product_qty for move in procurement.move_ids if move.state == 'done' ]) qty_done_proc_uom = self.env['product.uom']. \ _compute_qty_obj(procurement.product_id.uom_id, qty_done, procurement.product_uom) if procurement.product_uos: qty_done_proc_uos = float_round( self.env['product.uom']._compute_qty_obj( procurement.product_id.uom_id, qty_done, procurement.product_uos), precision_rounding=procurement.product_uos.rounding) else: qty_done_proc_uos = float_round( qty_done_proc_uom, precision_rounding=procurement.product_uom.rounding) if float_compare(qty_done, 0.0, precision_rounding=procurement.product_id. uom_id.rounding) > 0: procurement.write({ 'product_qty': float_round(qty_done_proc_uom, precision_rounding=procurement.product_uom. rounding), 'product_uos_qty': qty_done_proc_uos, })
class account_invoice(osv.Model): _inherit = 'account.invoice' def get_discount_payment_invoice(self, cr, uid, invoice_id, date, context=None): """ Obtiene el descuento que corresponde sobre la factura """ disc_obj = self.pool.get('product.pricelist.discount') item_obj = self.pool.get('product.pricelist.discount.item') inv = self.browse(cr, uid, invoice_id, context=context) amount = inv.type in ('out_refund', 'in_refund') and -inv.residual or inv.residual # Obtiene el descuento que se debe aplicar por pronto pago discount = 0.0 discount_amount = 0.0 # Valida que la factura sea una factura de cliente y que tenga un precio de lista if inv.type == 'out_invoice' and inv.pricelist_id: # Obtiene los datos de la factura inv_date = inv.date_invoice pricelist = inv.pricelist_id.id # Obtiene los descuentos disponibles en donde se aplican pagos type_ids = self.pool.get('product.pricelist.discount.type').search( cr, uid, [('to_paid', '=', True)], context=context) # Obtiene el numero de dias en relacion al pago contra la factura days = disc_obj.get_number_days( cr, uid, inv_date, date, context=context) - 1 # Valida si se debe aplicar un descuento sobre pronto pago discount_ids = disc_obj.search(cr, uid, [('type_id', 'in', type_ids), ('pricelist_id', '=', pricelist)]) if discount_ids: # Recorre los descuentos sobre pagos y valida si se aplican en alguna de las reglas for disc in disc_obj.browse(cr, uid, discount_ids, context=context): # Valida que el descuento tenga un codigo if not disc.type_id.key: continue # Recorre las reglas aplicadas sobre el descuento for item in disc.item_ids: apply_discount = False # Valida si hay categorias que excluyen el descuento if item_obj.validate_exception_categ(cr, uid, item.id, inv.id, context=context): continue # Valida que los dias de la factura al pago correspondan al descuento if item.min_quantity >= days: discount += item.discount break # Si el descuento es mayor a cero obtiene el valor de la factura if discount > 0.0: # Obtiene el monto del descuento discount_amount = (inv.amount_total - inv.discount_sale) * (discount / 100) # Trunca el descuento a dos digitos discount_amount = float(int(discount_amount * 100.0)) / 100.0 amount += inv.type in ( 'out_refund', 'in_refund') and discount_amount or -discount_amount return discount_amount, amount def generate_voucher_invoice(self, cr, uid, invoice_id, refund_id, context=None): """ Genera el cobro automatico de una factura sobre una nota de credito """ inv_obj = self.pool.get('account.invoice') v_obj = self.pool.get('account.voucher') v_line_obj = self.pool.get('account.voucher.line') move_line_obj = self.pool.get('account.move.line') journal_pool = self.pool.get('account.journal') total_credit = 0.0 total_debit = 0.0 account_type = 'receivable' type_line = 'receipt' # Obtiene la informacion de la factura inv = inv_obj.browse(cr, uid, invoice_id, context=context) if inv.type in ['in_invoice', 'in_refund']: account_type = 'payable' type_line = 'payment' else: account_type = 'receivable' type_line = 'receipt' reconcile = 0 to_reconcile_ids = [] # Recorre las lineas de la factura y obtiene los movimientos a pagar de la factura movelines = inv.move_id.line_id for line in movelines: # Si la factura origen ya esta pagada en su totalidad se omite el proceso if (line.account_id.id == inv.account_id.id) and (type( line.reconcile_id) == osv.orm.browse_null): to_reconcile_ids.append(line.id) reconcile += 1 total_credit += line.credit or 0.0 total_debit += line.debit or 0.0 # Si no se encontraron lineas para conciliar termina el proceso if reconcile == 0: return True # Obtiene la informacion de la nota de credito refund = inv_obj.browse(cr, uid, refund_id, context=context) reconcile = 0 # Recorre las lineas de la nota de credito y obtiene los movimientos a pagar de la factura movelines = refund.move_id.line_id for line in movelines: # Si la factura origen ya esta pagada en su totalidad se omite el proceso if (line.account_id.id == refund.account_id.id) and (type( line.reconcile_id) == osv.orm.browse_null): to_reconcile_ids.append(line.id) reconcile += 1 total_credit += line.credit or 0.0 total_debit += line.debit or 0.0 # Si no se encontraron lineas para conciliar termina el proceso if reconcile == 0: return True # Obtiene la cuenta que se va a aplicar para el voucher account_id = inv.partner_id.property_account_receivable.id line_cr_ids = [] line_dr_ids = [] voucher_line = [] concilie = 0 # Recorre las lineas de movimiento y crea las lineas del voucher for line in move_line_obj.browse(cr, uid, to_reconcile_ids, context=context): amount_unreconciled = abs(line.amount_residual_currency) reconcile = False if line.credit: amount = min(amount_unreconciled, abs(total_debit)) total_debit -= amount else: amount = min(amount_unreconciled, abs(total_credit)) total_credit -= amount line_type = line.credit and 'dr' or 'cr' # Revisa si se esta conciliando el movimiento completo if amount_unreconciled == amount and concilie == 0: reconcile = True if total_credit == 0 or total_debit == 0: concilie = 1 # Genera un arreglo con la informacion de la linea a generar rs = { 'name': line.move_id.name, 'type': line_type, 'move_line_id': line.id, 'account_id': line.account_id.id, 'amount_original': abs(line.amount_currency), 'amount': amount, 'date_original': line.date, 'date_due': line.date_maturity, 'amount_unreconciled': amount_unreconciled, 'currency_id': inv.currency_id.id, 'reconcile': reconcile } # Agrega la informacion a los cargos o abonos if rs['type'] == 'cr': line_cr_ids.append(rs) else: line_dr_ids.append(rs) voucher_line.append(rs) writeoff_amount = v_obj._compute_writeoff_amount( cr, uid, line_dr_ids, line_cr_ids, 0.0, type_line) vals = { 'invoice_id': inv.id, 'partner_id': inv.partner_id.id, 'currency_id': inv.currency_id.id, 'type': type_line, 'pre_line': 1, 'writeoff_amount': writeoff_amount, 'account_id': account_id } # Crea el nuevo voucher para la factura v_id = v_obj.create(cr, uid, vals) # Crea las lineas del voucher for line in voucher_line: line['voucher_id'] = v_id v_line_obj.create(cr, uid, line, context=context) # Aplica el pago sobre los movimientos wf_service = netsvc.LocalService("workflow") wf_service.trg_validate(uid, 'account.voucher', v_id, 'proforma_voucher', cr) return v_id def _create_refund_to_discount(self, cr, uid, invoice, context=None): """ Crea la nota de credito donde se aplica el descuento comercial, volumen y mezcla """ obj_journal = self.pool.get('account.journal') cur_obj = self.pool.get('res.currency') inv_obj = self.pool.get('account.invoice') inv_line_obj = self.pool.get('account.invoice.line') if context is None: context = {} type_dict = { 'out_invoice': 'out_refund', # Customer Invoice 'in_invoice': 'in_refund', # Supplier Invoice 'out_refund': 'out_invoice', # Customer Refund 'in_refund': 'in_invoice', # Supplier Refund } invoice_data = {} for field in [ 'name', 'reference', 'comment', 'date_due', 'partner_id', 'company_id', 'account_id', 'currency_id', 'payment_term', 'user_id', 'fiscal_position' ]: if invoice._all_columns[field].column._type == 'many2one': invoice_data[field] = invoice[field].id else: invoice_data[ field] = invoice[field] if invoice[field] else False tax_lines = filter(lambda l: l['manual'], invoice.tax_line) tax_lines = inv_obj._refund_cleanup_lines(cr, uid, tax_lines, context=context) # Obtenemos el diario para la nota de debito refund_journal = invoice.journal_id.journal_refund_id # Valida que exista if not refund_journal: raise osv.except_osv( _('Error!'), _("Diario automatico para nota de debito, no configurado, configure el diario sobre diario de credito" )) if refund_journal.type not in ('sale_refund', 'purchase_refund'): raise osv.except_osv( _('Error!'), _("Configurar el diario de nota de debito con: credito de compra o credito de venta" )) date = time.strftime('%Y-%m-%d') invoice_data.update({ 'type': type_dict[invoice['type']], 'date_invoice': date, 'state': 'draft', 'number': False, #'tax_line': tax_lines, 'journal_id': refund_journal.id or False, 'invoice_id': invoice.id }) # Agrega el campo de tipo de nota de credito si lo trae entre los parametros if context.get('default_filter_refund', False): invoice_data['filter_refund'] = context.get( 'default_filter_refund', False) invoice_id = inv_obj.create(cr, uid, invoice_data, context=context) cur = invoice.currency_id # Recorre las lineas de la factura for line in invoice.invoice_line: # Obtiene el monto del descuento de venta por la linea price = line.price_subtotal amount_discount = 0.0 discount_sale = 0.0 # Aplica al subtotal el descuento comercial y al impuesto if line.discount_com > 0: discount_sale = price * (line.discount_com / 100) price = price - discount_sale amount_discount += discount_sale # Aplica al subtotal el descuento de mezcla y al impuesto if line.discount_mez > 0: discount_sale = price * (line.discount_mez / 100) price = price - discount_sale amount_discount += discount_sale # Aplica al subtotal el descuento de volumen y al impuesto if line.discount_vol > 0: discount_sale = price * (line.discount_vol / 100) price = price - discount_sale amount_discount += discount_sale # Valida que el descuento no sea cero if amount_discount == 0.0: continue #tax = line. amount_discount = cur_obj.round(cr, uid, cur, amount_discount) vals = { 'product_id': line.product_id.id or False, 'name': 'Descuento (Comercial: %s %%, Volumen: %s %%, Mezcla: %s %%) - %s ' % (line.discount_com, line.discount_vol, line.discount_mez, line.name), 'quantity': 1, 'uos_id': line.uos_id.id, 'price_unit': amount_discount, #'tax_line': tax_lines, 'account_id': line.account_id.id or False, 'invoice_line_tax_id': [(6, 0, [x.id for x in line.invoice_line_tax_id])], 'invoice_id': invoice_id } #print "****************** vals despues de agregar tax de create refund *************",vals inv_line_obj.create(cr, uid, vals, context=context) # Actualiza los totales de la factura inv_obj.write(cr, uid, [invoice_id], {}, context=context) return invoice_id def invoice_validate(self, cr, uid, ids, context=None): """ Si la factura tiene descuentos genera la nota de credito """ wf_service = netsvc.LocalService('workflow') # Funcion original de validar factura res = super(account_invoice, self).invoice_validate(cr, uid, ids, context=context) # Revisa si alguna factura es de nota de credito y ve si tiene que aplicar una modificacion sobre la factura origen for inv in self.browse(cr, uid, ids, context=context): if inv.amount_total == 0.0 and inv.type in [ 'out_refund', 'in_refund' ]: raise osv.except_osv( _('Error!'), _("No puede validar notas de credito en cero: '%s'!") % (inv.number, )) # Valida que sea una nota de credito generada para el cliente if inv.type in ['out_invoice']: # Valida si hay descuento sobre la factura if inv.discount_sale: # Crea la nota de credito con la informacion del descuento financiero refund_id = self._create_refund_to_discount( cr, uid, inv, context=context) # Pasa la factura a abierto wf_service.trg_validate(uid, 'account.invoice', \ refund_id, 'invoice_open', cr) # Salda la nota de credito con la factura self.generate_voucher_invoice(cr, uid, inv.id, refund_id, context=context) # Actualiza el total de descuento sobre la venta obtenido por la nota de credito refund = self.browse(cr, uid, refund_id, context=context) self.write(cr, uid, [inv.id], {'discount_sale': refund.amount_total}) return res def invoice_pay_customer(self, cr, uid, ids, context=None): """ Abre el formulario para los cobros desde la factura """ if not ids: return [] dummy, view_id = self.pool.get('ir.model.data').get_object_reference( cr, uid, 'account_voucher', 'view_vendor_receipt_dialog_form') date = time.strftime('%Y-%m-%d') inv = self.browse(cr, uid, ids[0], context=context) # Obtiene el descuento sobre la factura discount_amount, amount = self.get_discount_payment_invoice( cr, uid, inv.id, date, context=context) return { 'name': _("Pay Invoice"), 'view_mode': 'form', 'view_id': view_id, 'view_type': 'form', 'res_model': 'account.voucher', 'type': 'ir.actions.act_window', 'nodestroy': True, 'target': 'new', 'domain': '[]', 'context': { 'payment_expected_currency': inv.currency_id.id, 'default_partner_id': self.pool.get('res.partner')._find_accounting_partner( inv.partner_id).id, 'default_amount': amount, 'default_discount': discount_amount, 'default_invoice_amount': inv.residual, 'default_reference': inv.name, 'close_after_process': True, 'invoice_type': inv.type, 'invoice_id': inv.id, 'default_type': inv.type in ('out_invoice', 'out_refund') and 'receipt' or 'payment', 'type': inv.type in ('out_invoice', 'out_refund') and 'receipt' or 'payment' } } def onchange_partner_id(self, cr, uid, ids, type, partner_id, date_invoice=False, payment_term=False, partner_bank_id=False, company_id=False): """ Cuando se modifique el cliente tiene que actualizar el precio de lista que contenga """ # Funcion original onchange res = super(account_invoice, self).onchange_partner_id(cr, uid, ids, type, partner_id, date_invoice, payment_term, partner_bank_id, company_id) #~ # Si no esta el metodo de pago relacionado con el cliente pone el metodo de no identificado #~ if partner_id: #~ partner = self.pool.get('res.partner').browse(cr, uid, partner_id) #~ # Si el cliente tiene una lista de precio relacionada, actualiza el valor en la factura #~ if partner.property_product_pricelist: #~ res['value']['pricelist_id'] = partner.property_product_pricelist.id return res _columns = { 'pricelist_id': fields.many2one('product.pricelist', 'Tarifa'), 'discount_fin': fields.float('Descuento', digits_compute=dp.get_precision('Account')), 'discount_sale': fields.float('Descuento', digits_compute=dp.get_precision('Account')), 'tax_line': fields.one2many('account.invoice.tax', 'invoice_id', 'Tax Lines', readonly=True, states={'draft': [('readonly', False)]}), }
class proforma_invoice(osv.osv): def _amount_all(self, cr, uid, ids, name, args, context=None): res = {} tax_obj = self.pool.get('account.tax') 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 } for line in invoice.invoice_line: res[invoice.id]['amount_untaxed'] += line.price_subtotal for line in invoice.invoice_line: taxes = tax_obj.compute_all(cr, uid, line.invoice_line_tax_id, line.price_unit, line.quantity, product=line.product_id, partner=line.invoice_id.partner_id) res[invoice.id][ 'amount_tax'] += taxes['total_included'] - taxes['total'] res[invoice.id]['amount_total'] = res[ invoice.id]['amount_tax'] + res[invoice.id]['amount_untaxed'] return res def _get_sequence(self, cr, uid, ids, name, args, context=None): res = {} sale_obj = self.pool.get('sale.order') for invoice in self.browse(cr, uid, ids, context=context): seq = False if invoice.sale_id: curr_pi = invoice.sale_id.proforma_ids curr_seq_pi = [x.sequence for x in curr_pi] len_curr_seq = max(curr_seq_pi) if not invoice.sequence: seq = len_curr_seq + 1 if not invoice.sequence: res[invoice.id] = seq else: res[invoice.id] = invoice.sequence return res _name = "proforma.invoice" _description = 'Proforma Invoice' _order = "id desc" _columns = { 'sequence' : fields.function(_get_sequence, type="integer", string='Sequence Number', store=True), 'name': fields.char('Proforma Invoice', size=64, select=True, readonly=True), 'sale_id' : fields.many2one('sale.order','Sales Contract'), # 'origin': fields.char('Source Document', size=64, help="Reference of the document that produced this proforma invoice."), 'note': fields.text('Internal Information'), 'type': fields.selection([ ('out_invoice','Customer Invoice'), ('in_invoice','Supplier Invoice'), ],'Type', readonly=True, select=True, change_default=True, track_visibility='always'), # 'state': fields.selection([ # ('draft','Draft'), # ('proforma','Pro-forma'), # ('cancel','Cancelled'), # ],'Status', select=True, readonly=True, track_visibility='onchange', # help=' * The \'Draft\' status is used when a user is encoding a new and unconfirmed Invoice. \ # \n* The \'Pro-forma\' when invoice is in Pro-forma status,invoice does not have an invoice number. \ # \n* The \'Cancelled\' status is used when user cancel invoice.'), 'sent': fields.boolean('Sent', readonly=True, help="It indicates that the proforma invoice has been sent."), 'date_invoice': fields.date('Invoice Date', readonly=False, select=True, help="Keep empty to use the current date"), # 'date_due': fields.date('Due Date', readonly=True, states={'draft':[('readonly',False)]}, select=True, # help="If you use payment terms, the due date will be computed automatically at the generation "\ # "of accounting entries. The payment term may compute several due dates, for example 50% now and 50% in one month, but if you want to force a due date, make sure that the payment term is not set on the invoice. If you keep the payment term and the due date empty, it means direct payment."), 'partner_id': fields.many2one('res.partner', 'Buyer', change_default=True, readonly=False, required=True, track_visibility='always'), 'p_address_text' : fields.text('Buyer Address Custom'), 'p_use_custom_address' : fields.boolean('Use Custom Buyer Address?'), 'consignee_partner_id': fields.many2one('res.partner', 'Consignee', change_default=True, readonly=False, required=True, track_visibility='always'), 'c_address_text' : fields.text('Consignee Address Custom'), 'c_use_custom_address' : fields.boolean('Use Custom Consignee Address?'), 'notify_partner_id': fields.many2one('res.partner', 'Notify', change_default=True, readonly=False, required=False, track_visibility='always'), 'n_address_text' : fields.text('Notify Address Custom'), 'n_use_custom_address' : fields.boolean('Use Custom Notify Address?'), 'shipper_id': fields.many2one('res.partner', 'Shipper', change_default=True, readonly=False, required=True, track_visibility='always'), 's_address_text' : fields.text('Shipper Address Custom'), 's_use_custom_address' : fields.boolean('Use Custom Shipper Address?'), 'payment_term': fields.many2one('account.payment.term', 'Payment Terms',readonly=False, help="If you use payment terms, the due date will be computed automatically at the generation "\ "of accounting entries. If you keep the payment term and the due date empty, it means direct payment. "\ "The payment term may compute several due dates, for example 50% now, 50% in one month."), # 'tax_line': fields.one2many('proforma.invoice.tax', 'invoice_id', 'Tax Lines', readonly=True, states={'draft':[('readonly',False)]}), 'invoice_line': fields.one2many('proforma.invoice.line', 'invoice_id', 'Invoice Lines', readonly=False), 'amount_untaxed': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Subtotal', track_visibility='always', multi='all'), 'amount_tax': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Tax', multi='all'), 'amount_total': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Total', multi='all'), 'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=False, track_visibility='always'), 'company_id': fields.many2one('res.company', 'Company', required=True, change_default=True, readonly=False), 'remit_to' : fields.many2one('res.bank', 'Remit To', help=''), 'credit_to' : fields.many2one('res.bank', 'Credit To', help=''), 'company_bank_account' : fields.many2one('res.partner.bank', 'Bank Account', help='Bitratex Bank Account'), 'user_id': fields.many2one('res.users', 'Salesperson', readonly=False, track_visibility='onchange'), } def _invoice_line_for(self, cr, uid, order_line): invoice_line = { 'name': order_line.name, # 'origin': fields.char('Source Document', size=256, help="Reference of the document that produced this proforma invoice."), # 'sequence': fields.integer('Sequence', help="Gives the sequence of this line when displaying the invoice."), 'uom_id': order_line.product_uom.id, 'product_id': order_line.product_id.id, 'price_unit': order_line.price_unit, 'invoice_line_tax_id': [(6, 0, [x.id for x in order_line.tax_id])], 'quantity': order_line.product_uom_qty, } if order_line.order_id.sale_type == 'export': invoice_line['name'] = order_line.export_desc return invoice_line def default_get(self, cr, uid, fields, context): if context is None: context = {} res = super(proforma_invoice, self).default_get(cr, uid, fields, context=context) sale_id = context.get('sale_id', False) # active_model = context.get('active_model') # assert active_model in ('sale.order'), 'Bad context propagation' if sale_id: sale = self.pool.get('sale.order').browse(cr, uid, sale_id) if 'sale_id' in fields: res.update(sale_id=sale.id) if 'partner_id' in fields: res.update(partner_id=sale.partner_id.id) if 'consignee_partner_id' in fields: res.update( consignee_partner_id=sale.consignee and sale.consignee.id or sale.partner_invoice_id and sale.partner_invoice_id.id or sale.partner_id.id) if 'notify_partner_id' in fields: res.update( notify_partner_id=sale.notify and sale.notify.id or False) if 'payment_term' in fields: res.update(payment_term=False) if 'currency_id' in fields: res.update( payment_term=sale.currency_id and sale.currency_id.id or False) if 'company_id' in fields: res.update( payment_term=sale.currency_id and sale.currency_id.id or False) if 'date_invoice' in fields: res.update(date=time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)) # if 'opening_bank' in fields: # res.update(payment_term=sale.opening_bank and sale.opening_bank.id or False) # if 'intermed_bank' in fields: # res.update(payment_term=sale.intermed_bank and sale.intermed_bank.id or False) # if 'negotiate_bank' in fields: # res.update(payment_term=sale.negotiate_bank and sale.negotiate_bank.id or False) if 'invoice_line' in fields: line_ids = [ self._invoice_line_for(cr, uid, order_line) for order_line in sale.order_line if order_line.state not in ('done') ] res.update(invoice_line=line_ids) return res _defaults = { 'type': 'out_invoice', 'date_invoice': lambda *a: time.strftime('%Y-%m-%d'), 'currency_id': lambda self, cr, uid, context: self.pool.get('res.users').browse( cr, uid, uid, context).company_id.currency_id.id or False, 'company_id': lambda self, cr, uid, c: self.pool.get('res.company'). _company_default_get(cr, uid, 'account.invoice', context=c), 'shipper_id': lambda self, cr, uid, c: self.pool.get('res.users').browse( cr, uid, uid, c).company_id.partner_id.id or False, 'user_id': lambda s, cr, u, c: u, 'sent': False, } def create(self, cr, uid, vals, context=None): res = super(proforma_invoice, self).create(cr, uid, vals, context=context) pi_id = self.browse(cr, uid, res) if pi_id: company_code = '' if pi_id.company_id: company_code = pi_id.company_id.name[0:1] if pi_id.sale_id: if pi_id.sale_id.sale_type == 'export': self.write( cr, uid, res, { 'name': (company_code + 'E ' + (self.pool.get('ir.sequence').get( cr, uid, 'proforma.invoice.export') or '/')) }) elif pi_id.sale_id.sale_type == 'local': self.write( cr, uid, res, { 'name': (company_code + 'L ' + (self.pool.get('ir.sequence').get( cr, uid, 'proforma.invoice.local') or '/')) }) else: self.write( cr, uid, res, { 'name': (company_code + ' ' + (self.pool.get('ir.sequence').get( cr, uid, 'proforma.invoice') or '/')) }) return res def get_address(self, partner_obj): if partner_obj: partner_address = '' partner_address += partner_obj.street and partner_obj.street + '\n ' or '' partner_address += partner_obj.street2 and partner_obj.street2 + '\n ' or '' partner_address += partner_obj.street3 and partner_obj.street3 + '\n ' or '' partner_address += partner_obj.city and partner_obj.city + ' ' or '' partner_address += partner_obj.zip and partner_obj.zip + ', ' or '' partner_address += partner_obj.country_id.name and partner_obj.country_id.name or '' return partner_address else: return False def onchange_check(self, cr, uid, ids, partner_id, context=None): if context is None: context = {} if not partner_id: return {'value': {}} result = {} partner = self.pool.get('res.partner').browse(cr, uid, partner_id) if context.get('shipper', False) and partner: result.update({'s_address_text': self.get_address(partner)}) if context.get('consignee', False) and partner: result.update({'c_address_text': self.get_address(partner)}) if context.get('partner', False) and partner: result.update({'p_address_text': self.get_address(partner)}) if context.get('notify', False) and partner: result.update({'n_address_text': self.get_address(partner)}) return {'value': result}
# -*- coding: utf-8 -*- import logging import time from osv import osv, fields import openerp.addons.decimal_precision as dp DP = dp.get_precision('Budget') class BudgetBudget(osv.osv): """ Implementacion de clase de Presupuesto """ _name = 'budget.budget' _order = 'code' _columns = { 'code': fields.char('Código', size=64, required=True), 'name': fields.char('Presupuesto', size=128, required=True), 'department_id': fields.many2one('hr.department', string='Departamento', required=True), 'date_start': fields.date('Fecha Inicio', required=True), 'date_end': fields.date('Fecha Fin', required=True), 'state': fields.selection( [('draft','Borrador'), ('open','Ejecución'), ('close','Cerrado')], string='Estado',
class AgreementLine(models.Model): _name = 'account.periodical_invoicing.agreement.line' _order = 'sequence, id' sequence = fields.Integer(default=10) active_chk = fields.Boolean( string='Active', default=True, help='Unchecking this field, this quota is not generated') agreement_id = fields.Many2one('account.periodical_invoicing.agreement', 'Agreement reference', ondelete='cascade') product_id = fields.Many2one('product.product', 'Product', ondelete='set null', required=True) name = fields.Char(related='product_id.name', string='Description', store=False) additional_description = fields.Text( string='Add. description', help='Additional description that will be added to the product ' 'description on invoices.') quantity = fields.Float(string='Quantity', required=True, default=1, help='Quantity of the product to invoice') price = fields.Float( 'Product price', digits_compute=dp.get_precision('Account'), help='Specific price for this product. Keep empty to use the ' 'current price while generating invoice') discount = fields.Float('Discount (%)', digits=(16, 2)) invoicing_interval = fields.Integer( string='Interval', required=True, default=1, help="Interval in time units for invoicing this product") invoicing_unit = fields.Selection(selection=[('days', 'days'), ('weeks', 'weeks'), ('months', 'months'), ('years', 'years')], string='Interval unit', required=True, default='months') last_invoice_date = fields.Date(string='Last invoice') _sql_constraints = [ ('line_qty_zero', 'CHECK (quantity > 0)', 'All product quantities must be greater than 0.\n'), ('line_interval_zero', 'CHECK (invoicing_interval > 0)', 'All invoicing intervals must be greater than 0.\n'), ] def copy(self, cr, uid, orig_id, default=None, context=None): if default is None: default = {} default.update({ 'last_invoice_date': False, }) return super(AgreementLine, self).copy(cr, uid, orig_id, default, context=context) def onchange_product_id(self, cr, uid, ids, product_id=False, context=None): result = {} if product_id: product = self.pool['product.product'].browse(cr, uid, product_id, context=context) if product: result['value'] = {'name': product['name']} return result
'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'), <<<<<<< HEAD 'comm_code': fields.char('Commodity Code', size=256,), ======= # 'comm_code': fields.char('Commodity Code', size=256,), 'comm_code':fields.many2one('ups.commodity.code','Commodity Code'), >>>>>>> c1979f64b3360c86d60e00c92be0271d89f97f2d '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, 'exp_carrier':"United Parcel Service" }
class productSimplePricelistItemUpdateWizard(TransientModel): _name = 'product.simple.pricelist.item.update.wizard' # Default Get Section def default_get(self, cr, uid, fields, context=None): pp_obj = self.pool['product.product'] pplv_obj = self.pool['product.pricelist.version'] res = super(productSimplePricelistItemUpdateWizard, self).default_get(cr, uid, fields, context=context) pp = pp_obj.browse(cr, uid, context['product_id'], context=context) pplv = pplv_obj.browse(cr, uid, context['pricelist_version_id'], context=context) res.update({ 'product_id': pp.id, 'list_price': pp.list_price, 'pricelist_id': pplv.pricelist_id.id, 'pricelist_version_id': pplv.id, }) return res # Column Section _columns = { 'list_price': fields.float('List Price', required=True, readonly=True, digits_compute=dp.get_precision('Product Price')), 'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', required=True, readonly=True), 'pricelist_version_id': fields.many2one('product.pricelist.version', 'Pricelist Version', required=True, readonly=True), 'product_id': fields.many2one('product.product', 'Product', required=True, readonly=True), 'specific_price': fields.float('Price', required=True, digits_compute=dp.get_precision('Product Price')), } # Button Section def set_price(self, cr, uid, ids, context=None): pp_obj = self.pool['product.product'] pplv_obj = self.pool['product.pricelist.version'] ppli_obj = self.pool['product.pricelist.item'] for pspiuw in self.browse(cr, uid, ids, context=context): pp = pp_obj.browse(cr, uid, pspiuw.product_id.id, context=context) pplv = pplv_obj.browse(cr, uid, pspiuw.pricelist_version_id.id, context=context) item_id = False for item in pplv.items_id: if item.product_id.id == pp.id: item_id = item.id if item_id: if pspiuw.specific_price == 0: # Unlink specific price ppli_obj.unlink(cr, uid, [item_id], context=context) # Update specific price ppli_obj.write(cr, uid, [item_id], { 'price_surcharge': pspiuw.specific_price, }, context=context) else: # Create new item ppli_obj.create(cr, uid, { 'name': pp.code, 'product_id': pspiuw.product_id.id, 'company_id': pplv.company_id.id, 'price_version_id': pplv.id, 'base': 1, 'price_discount': -1, 'price_surcharge': pspiuw.specific_price, }, context=context) return True
import time from openerp.osv import orm, fields from openerp.addons 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_category_id': fields.many2one('l10n_br_account.fiscal.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(orm.Model): _inherit = 'account.fiscal.position.rule.template'
class account_invoice_line(osv.Model): _inherit = "account.invoice.line" def _amount_line_tax(self, cr, uid, line, price, context=None): """ Calculando el subtotal con impuestos """ val = 0.0 # Se llama para redondear la cantindad cur_obj = self.pool.get('res.currency') # Calculando impuestos for c in self.pool.get('account.tax').compute_all( cr, uid, line.invoice_line_tax_id, price, 1, line.product_id, line.partner_id)['taxes']: val += c.get('amount', 0.0) cur = line.invoice_id.currency_id or 0.0 print """""" """""" """"cur: """, cur if cur: # Regresando el valor de los impuestos redondeado return cur_obj.round(cr, uid, cur, val) else: return 0.0 def _compute_tax_all(self, cr, uid, ids, field_names, args, context=None): """ Calculando importe con descuentos: comercial, volumen, mezcla; además del cálculo del total neto con descuento financiero """ res = {} amount_subtotal_desc = 0.0 discount_subtotal_sale = 0.0 price_discount = 0.0 imprt_discount = 0.0 dif = 0.0 utility_c = 0.0 utility_div = 0.0 utility_p = 0.0 for line in self.browse(cr, uid, ids, context=context): res[line.id] = { 'price_unit_discount': 0.0, 'price_subtotal2': 0.0, 'diference': 0.0, 'total_cost': 0.0, 'utility': 0.0, 'utility_percent': 0.0, } # Se obtiene el importe o subtotal price = line.price_subtotal # Aplica al subtotal el descuento comercial if line.discount_com > 0: discount_sale = price * (line.discount_com / 100) price = price - discount_sale # Aplica al subtotal el descuento de mezcla if line.discount_mez > 0: discount_sale = price * (line.discount_mez / 100) price = price - discount_sale # Aplica al subtotal el descuento de volumen if line.discount_vol > 0: discount_sale = price * (line.discount_vol / 100) price = price - discount_sale # Aplica al subtotal el descuento de financiero if line.discount_fin > 0: discount_sale = price * (line.discount_fin / 100) price = price - discount_sale print "********** PRICE ********* ", price # Se agrega el nuevo importe con los descuentos (comercial, volumen, mezcla) al diccionario res[line.id]['price_subtotal2'] = price # Calculamos el precio del producto con descuentos aplicados res[line.id]['price_unit_discount'] = (price / line.quantity) # Calculamos la diferencia de importes res[line.id]['diference'] = line.price_subtotal - res[ line.id]['price_subtotal2'] # Obtiene el costo total res[line.id]['total_cost'] = line.standard_price * line.quantity # Calcula la utilidad utility = price - res[line.id]['total_cost'] res[line.id]['utility'] = utility res[line.id]['utility_percent'] = ( utility / price) * 100 if utility != 0 and price != 0 else 0.0 return res _columns = { # Descuentos aplicados sobre la linea 'discount_com': fields.float('Comercial', digits_compute=dp.get_precision('Account'), readonly=True), 'discount_vol': fields.float('Volumen', digits_compute=dp.get_precision('Account'), readonly=True), 'discount_mez': fields.float('Mezcla', digits_compute=dp.get_precision('Account'), readonly=True), 'discount_fin': fields.float('Financiero', digits_compute=dp.get_precision('Account'), readonly=True), # Campos para reporte de ventas 'price_unit_discount': fields.function(_compute_tax_all, type='float', digits_compute=dp.get_precision('Account'), string='Precio C/Desc (unitario)', method=True, store=True, multi='amount_all'), 'price_subtotal2': fields.function(_compute_tax_all, type='float', digits_compute=dp.get_precision('Account'), string='Importe C/Desc', method=True, store=True, multi='amount_all'), 'diference': fields.function(_compute_tax_all, type='float', digits_compute=dp.get_precision('Account'), string='Diferencia', method=True, store=True, multi='amount_all'), 'total_cost': fields.function(_compute_tax_all, type='float', digits_compute=dp.get_precision('Account'), store=True, string='Total costo', method=True, multi="amount_all"), 'utility': fields.function(_compute_tax_all, type='float', digits_compute=dp.get_precision('Account'), string='Utilidad', store=True, method=True, multi="amount_all"), 'utility_percent': fields.function(_compute_tax_all, type='float', digits_compute=dp.get_precision('Account'), string='%Utilidad', method=True, store=True, multi='amount_all'), }
class product_product(osv.osv): _inherit = "product.product" def _stock_move_count(self, cr, uid, ids, field_name, arg, context=None): res = dict([(id, { 'reception_count': 0, 'delivery_count': 0 }) for id in ids]) move_pool = self.pool.get('stock.move') moves = move_pool.read_group(cr, uid, [('product_id', 'in', ids), ('picking_id.type', '=', 'in'), ('state', 'in', ('confirmed', 'assigned', 'pending'))], ['product_id'], ['product_id']) for move in moves: product_id = move['product_id'][0] res[product_id]['reception_count'] = move['product_id_count'] moves = move_pool.read_group(cr, uid, [('product_id', 'in', ids), ('picking_id.type', '=', 'out'), ('state', 'in', ('confirmed', 'assigned', 'pending'))], ['product_id'], ['product_id']) for move in moves: product_id = move['product_id'][0] res[product_id]['delivery_count'] = move['product_id_count'] return res def get_product_accounts(self, cr, uid, product_id, context=None): """ To get the stock input account, stock output account and stock journal related to product. @param product_id: product id @return: dictionary which contains information regarding stock input account, stock output account and stock journal """ if context is None: context = {} product_obj = self.pool.get('product.product').browse(cr, uid, product_id, context=context) stock_input_acc = product_obj.property_stock_account_input and product_obj.property_stock_account_input.id or False if not stock_input_acc: stock_input_acc = product_obj.categ_id.property_stock_account_input_categ and product_obj.categ_id.property_stock_account_input_categ.id or False stock_output_acc = product_obj.property_stock_account_output and product_obj.property_stock_account_output.id or False if not stock_output_acc: stock_output_acc = product_obj.categ_id.property_stock_account_output_categ and product_obj.categ_id.property_stock_account_output_categ.id or False journal_id = product_obj.categ_id.property_stock_journal and product_obj.categ_id.property_stock_journal.id or False account_valuation = product_obj.categ_id.property_stock_valuation_account_id and product_obj.categ_id.property_stock_valuation_account_id.id or False return { 'stock_account_input': stock_input_acc, 'stock_account_output': stock_output_acc, 'stock_journal': journal_id, 'property_stock_valuation_account_id': account_valuation } def do_change_standard_price(self, cr, uid, ids, datas, context=None): """ Changes the Standard Price of Product and creates an account move accordingly. @param datas : dict. contain default datas like new_price, stock_output_account, stock_input_account, stock_journal @param context: A standard dictionary @return: """ location_obj = self.pool.get('stock.location') move_obj = self.pool.get('account.move') move_line_obj = self.pool.get('account.move.line') if context is None: context = {} new_price = datas.get('new_price', 0.0) stock_output_acc = datas.get('stock_output_account', False) stock_input_acc = datas.get('stock_input_account', False) journal_id = datas.get('stock_journal', False) move_ids = [] loc_ids = location_obj.search(cr, uid, [('usage', '=', 'internal')]) for product in self.browse(cr, uid, ids, context=context): if product.valuation != 'real_time': continue account_valuation = product.categ_id.property_stock_valuation_account_id account_valuation_id = account_valuation and account_valuation.id or False if not account_valuation_id: raise osv.except_osv( _('Error!'), _('Specify valuation Account for Product Category: %s.') % (product.categ_id.name)) for location in location_obj.browse(cr, uid, loc_ids, context=context): c = context.copy() c.update({'location': location.id, 'compute_child': False}) # qty_available depends of the location in the context qty = self.read(cr, uid, [product.id], ['qty_available'], context=c)[0]['qty_available'] diff = product.standard_price - new_price if not diff: raise osv.except_osv( _('Error!'), _("No difference between standard price and new price!" )) if qty: company_id = location.company_id and location.company_id.id or False if not company_id: raise osv.except_osv( _('Error!'), _('Please specify company in Location.')) # # Accounting Entries # if not journal_id: journal_id = product.categ_id.property_stock_journal and product.categ_id.property_stock_journal.id or False if not journal_id: raise osv.except_osv(_('Error!'), _('Please define journal '\ 'on the product category: "%s" (id: %d).') % \ (product.categ_id.name, product.categ_id.id,)) move_id = move_obj.create(cr, uid, { 'journal_id': journal_id, 'company_id': company_id }) move_ids.append(move_id) if diff > 0: if not stock_input_acc: stock_input_acc = product.\ property_stock_account_input.id if not stock_input_acc: stock_input_acc = product.categ_id.\ property_stock_account_input_categ.id if not stock_input_acc: raise osv.except_osv(_('Error!'), _('Please define stock input account ' \ 'for this product: "%s" (id: %d).') % \ (product.name, product.id,)) amount_diff = qty * diff move_line_obj.create( cr, uid, { 'name': product.name, 'account_id': stock_input_acc, 'debit': amount_diff, 'move_id': move_id, }) move_line_obj.create( cr, uid, { 'name': product.categ_id.name, 'account_id': account_valuation_id, 'credit': amount_diff, 'move_id': move_id }) elif diff < 0: if not stock_output_acc: stock_output_acc = product.\ property_stock_account_output.id if not stock_output_acc: stock_output_acc = product.categ_id.\ property_stock_account_output_categ.id if not stock_output_acc: raise osv.except_osv(_('Error!'), _('Please define stock output account ' \ 'for this product: "%s" (id: %d).') % \ (product.name, product.id,)) amount_diff = qty * -diff move_line_obj.create( cr, uid, { 'name': product.name, 'account_id': stock_output_acc, 'credit': amount_diff, 'move_id': move_id }) move_line_obj.create( cr, uid, { 'name': product.categ_id.name, 'account_id': account_valuation_id, 'debit': amount_diff, 'move_id': move_id }) self.write(cr, uid, ids, {'standard_price': new_price}) return move_ids def view_header_get(self, cr, user, view_id, view_type, context=None): if context is None: context = {} res = super(product_product, self).view_header_get(cr, user, view_id, view_type, context) if res: return res if (context.get('active_id', False)) and (context.get('active_model') == 'stock.location'): return _('Products: ') + self.pool.get('stock.location').browse( cr, user, context['active_id'], context).name return res def get_product_available(self, cr, uid, ids, context=None): """ Finds whether product is available or not in particular warehouse. @return: Dictionary of values """ if context is None: context = {} location_obj = self.pool.get('stock.location') warehouse_obj = self.pool.get('stock.warehouse') shop_obj = self.pool.get('sale.shop') states = context.get('states', []) what = context.get('what', ()) if not ids: ids = self.search(cr, uid, []) res = {}.fromkeys(ids, 0.0) if not ids: return res if context.get('shop', False): warehouse_id = shop_obj.read(cr, uid, int(context['shop']), ['warehouse_id'])['warehouse_id'][0] if warehouse_id: context['warehouse'] = warehouse_id if context.get('warehouse', False): lot_id = warehouse_obj.read(cr, uid, int(context['warehouse']), ['lot_stock_id'])['lot_stock_id'][0] if lot_id: context['location'] = lot_id if context.get('location', False): if type(context['location']) == type(1): location_ids = [context['location']] elif type(context['location']) in (type(''), type(u'')): location_ids = location_obj.search( cr, uid, [('name', 'ilike', context['location'])], context=context) else: location_ids = context['location'] else: location_ids = [] wids = warehouse_obj.search(cr, uid, [], context=context) if not wids: return res for w in warehouse_obj.browse(cr, uid, wids, context=context): location_ids.append(w.lot_stock_id.id) # build the list of ids of children of the location given by id if context.get('compute_child', True): child_location_ids = location_obj.search( cr, uid, [('location_id', 'child_of', location_ids)]) location_ids = child_location_ids or location_ids # this will be a dictionary of the product UoM by product id product2uom = {} uom_ids = [] for product in self.read(cr, uid, ids, ['uom_id'], context=context): product2uom[product['id']] = product['uom_id'][0] uom_ids.append(product['uom_id'][0]) # this will be a dictionary of the UoM resources we need for conversion purposes, by UoM id uoms_o = {} for uom in self.pool.get('product.uom').browse(cr, uid, uom_ids, context=context): uoms_o[uom.id] = uom results = [] results2 = [] from_date = context.get('from_date', False) to_date = context.get('to_date', False) date_str = False date_values = False where = [ tuple(location_ids), tuple(location_ids), tuple(ids), tuple(states) ] if from_date and to_date: date_str = "date>=%s and date<=%s" where.append(tuple([from_date])) where.append(tuple([to_date])) elif from_date: date_str = "date>=%s" date_values = [from_date] elif to_date: date_str = "date<=%s" date_values = [to_date] if date_values: where.append(tuple(date_values)) prodlot_id = context.get('prodlot_id', False) prodlot_clause = '' if prodlot_id: prodlot_clause = ' and prodlot_id = %s ' where += [prodlot_id] # TODO: perhaps merge in one query. if 'in' in what: # all moves from a location out of the set to a location in the set cr.execute( 'select sum(product_qty), product_id, product_uom '\ 'from stock_move '\ 'where location_id NOT IN %s '\ 'and location_dest_id IN %s '\ 'and product_id IN %s '\ 'and state IN %s ' + (date_str and 'and '+date_str+' ' or '') +' '\ + prodlot_clause + 'group by product_id,product_uom',tuple(where)) results = cr.fetchall() if 'out' in what: # all moves from a location in the set to a location out of the set cr.execute( 'select sum(product_qty), product_id, product_uom '\ 'from stock_move '\ 'where location_id IN %s '\ 'and location_dest_id NOT IN %s '\ 'and product_id IN %s '\ 'and state in %s ' + (date_str and 'and '+date_str+' ' or '') + ' '\ + prodlot_clause + 'group by product_id,product_uom',tuple(where)) results2 = cr.fetchall() # Get the missing UoM resources uom_obj = self.pool.get('product.uom') uoms = map(lambda x: x[2], results) + map(lambda x: x[2], results2) if context.get('uom', False): uoms += [context['uom']] uoms = filter(lambda x: x not in uoms_o.keys(), uoms) if uoms: uoms = uom_obj.browse(cr, uid, list(set(uoms)), context=context) for o in uoms: uoms_o[o.id] = o #TOCHECK: before change uom of product, stock move line are in old uom. context.update({'raise-exception': False}) # Count the incoming quantities for amount, prod_id, prod_uom in results: amount = uom_obj._compute_qty_obj(cr, uid, uoms_o[prod_uom], amount, uoms_o[context.get('uom', False) or product2uom[prod_id]], context=context) res[prod_id] += amount # Count the outgoing quantities for amount, prod_id, prod_uom in results2: amount = uom_obj._compute_qty_obj(cr, uid, uoms_o[prod_uom], amount, uoms_o[context.get('uom', False) or product2uom[prod_id]], context=context) res[prod_id] -= amount return res def _product_available(self, cr, uid, ids, field_names=None, arg=False, context=None): """ Finds the incoming and outgoing quantity of product. @return: Dictionary of values """ if not field_names: field_names = [] if context is None: context = {} res = {} for id in ids: res[id] = {}.fromkeys(field_names, 0.0) for f in field_names: c = context.copy() if f == 'qty_available': c.update({'states': ('done', ), 'what': ('in', 'out')}) if f == 'virtual_available': c.update({ 'states': ('confirmed', 'waiting', 'assigned', 'done'), 'what': ('in', 'out') }) if f == 'incoming_qty': c.update({ 'states': ('confirmed', 'waiting', 'assigned'), 'what': ('in', ) }) if f == 'outgoing_qty': c.update({ 'states': ('confirmed', 'waiting', 'assigned'), 'what': ('out', ) }) stock = self.get_product_available(cr, uid, ids, context=c) for id in ids: res[id][f] = stock.get(id, 0.0) return res _columns = { 'reception_count': fields.function(_stock_move_count, string="Reception", type='integer', multi='pickings'), 'delivery_count': fields.function(_stock_move_count, string="Delivery", type='integer', multi='pickings'), 'qty_available': fields.function(_product_available, multi='qty_available', type='float', digits_compute=dp.get_precision('Product Unit of Measure'), 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 " "with 'internal' type."), 'virtual_available': fields.function(_product_available, multi='qty_available', type='float', digits_compute=dp.get_precision('Product Unit of Measure'), string='Forecasted Quantity', help="Forecast quantity (computed as Quantity On Hand " "- Outgoing + Incoming)\n" "In a context with a single Stock Location, this includes " "goods stored in 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 " "with 'internal' type."), 'incoming_qty': fields.function(_product_available, multi='qty_available', type='float', digits_compute=dp.get_precision('Product Unit of Measure'), 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 with 'internal' type."), 'outgoing_qty': fields.function(_product_available, multi='qty_available', type='float', digits_compute=dp.get_precision('Product Unit of Measure'), string='Outgoing', help="Quantity of products that are planned to leave.\n" "In a context with a single Stock Location, this includes " "goods leaving this Location, or any of its children.\n" "In a context with a single Warehouse, this includes " "goods leaving the Stock Location of this Warehouse, or " "any of its children.\n" "In a context with a single Shop, this includes goods " "leaving the Stock Location of the Warehouse of this " "Shop, or any of its children.\n" "Otherwise, this includes goods leaving any Stock " "Location with 'internal' type."), 'track_production': fields.boolean('Track Manufacturing Lots', help="Forces to specify a Serial Number for all moves containing this product and generated by a Manufacturing Order"), 'track_incoming': fields.boolean('Track Incoming Lots', help="Forces to specify a Serial Number for all moves containing this product and coming from a Supplier Location"), 'track_outgoing': fields.boolean('Track Outgoing Lots', help="Forces to specify a Serial Number for all moves containing this product and going to a Customer Location"), 'location_id': fields.dummy(string='Location', relation='stock.location', type='many2one'), 'warehouse_id': fields.dummy(string='Warehouse', relation='stock.warehouse', type='many2one'), 'valuation':fields.selection([('manual_periodic', 'Periodical (manual)'), ('real_time','Real Time (automated)'),], 'Inventory Valuation', help="If real-time valuation is enabled for a product, the system will automatically write journal entries corresponding to stock moves." \ "The inventory variation account set on the product category will represent the current inventory value, and the stock input and stock output account will hold the counterpart moves for incoming and outgoing products." , required=True), } _defaults = { 'valuation': 'manual_periodic', } def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): res = super(product_product, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar=toolbar, submenu=submenu) if context is None: context = {} if ('location' in context) and context['location']: location_info = self.pool.get('stock.location').browse( cr, uid, context['location']) fields = res.get('fields', {}) if fields: if location_info.usage == 'supplier': if fields.get('virtual_available'): res['fields']['virtual_available']['string'] = _( 'Future Receptions') if fields.get('qty_available'): res['fields']['qty_available']['string'] = _( 'Received Qty') if location_info.usage == 'internal': if fields.get('virtual_available'): res['fields']['virtual_available']['string'] = _( 'Future Stock') if location_info.usage == 'customer': if fields.get('virtual_available'): res['fields']['virtual_available']['string'] = _( 'Future Deliveries') if fields.get('qty_available'): res['fields']['qty_available']['string'] = _( 'Delivered Qty') if location_info.usage == 'inventory': if fields.get('virtual_available'): res['fields']['virtual_available']['string'] = _( 'Future P&L') if fields.get('qty_available'): res['fields']['qty_available']['string'] = _('P&L Qty') if location_info.usage == 'procurement': if fields.get('virtual_available'): res['fields']['virtual_available']['string'] = _( 'Future Qty') if fields.get('qty_available'): res['fields']['qty_available']['string'] = _( 'Unplanned Qty') if location_info.usage == 'production': if fields.get('virtual_available'): res['fields']['virtual_available']['string'] = _( 'Future Productions') if fields.get('qty_available'): res['fields']['qty_available']['string'] = _( 'Produced Qty') return res
class crossovered_budget_lines(models.Model): _inherit = "crossovered.budget.lines" @api.multi @api.depends('general_budget_id', 'general_budget_id.account_ids', 'date_to', 'date_from', 'analytic_account_id') def _get_practical_amount(self): account_obj = self.pool.get('account.account') for line in self: result = 0.0 acc_ids = [x.id for x in line.general_budget_id.account_ids] if not acc_ids: raise UserError( _("Error! The Budget '%s' has no accounts!") % ustr(line.general_budget_id.name)) acc_ids = account_obj._get_children_and_consol( self._cr, self._uid, acc_ids, context=self._context) date_to = line.date_to date_from = line.date_from crossovered_budget_id = line.crossovered_budget_id if crossovered_budget_id and isinstance(crossovered_budget_id.id, (NewId)): continue costcenter = crossovered_budget_id and crossovered_budget_id.cost_center_id or None if costcenter and date_from and date_to: self._cr.execute( "SELECT SUM(debit) - SUM(credit) FROM account_move_line WHERE cost_center_budget_id=%s AND (date " "between to_date(%s,'yyyy-mm-dd') AND to_date(%s,'yyyy-mm-dd')) AND " "account_id=%s ", (crossovered_budget_id.id, date_from, date_to, line.general_budget_id.account_id.id)) result = self._cr.fetchone()[0] elif line.analytic_account_id and date_from and date_to: self._cr.execute( "SELECT SUM(amount) FROM account_analytic_line WHERE account_id=%s AND (date " "between to_date(%s,'yyyy-mm-dd') AND to_date(%s,'yyyy-mm-dd')) AND " "general_account_id=ANY(%s)", ( line.analytic_account_id.id, date_from, date_to, acc_ids, )) result = self._cr.fetchone()[0] if result is None: result = 0.00 line.practical_amount = result practical_amount = fields.Float(compute='_get_practical_amount', string='Practical Amount', digits_compute=dp.get_precision('Account')) @api.multi def unlink(self): for line in self: result = None crossovered_budget_id = line.crossovered_budget_id date_to = line.date_to date_from = line.date_from costcenter = crossovered_budget_id and crossovered_budget_id.cost_center_id or None if costcenter and date_from and date_to: self._cr.execute( "SELECT id FROM account_move_line WHERE cost_center_budget_id=%s AND (date " "between to_date(%s,'yyyy-mm-dd') AND to_date(%s,'yyyy-mm-dd')) AND " "account_id=%s ", (crossovered_budget_id.id, date_from, date_to, line.general_budget_id.account_id.id)) result = self._cr.fetchone() if result: warning_msg = _( 'Is not possible to delete the budget line because journal entries are posted on the account!' ) raise UserError(warning_msg) super(crossovered_budget_lines, line).unlink() return True
class stock_move(osv.osv): _inherit = 'stock.move' def _cal_move_weight(self, cr, uid, ids, name, args, context=None): res = {} uom_obj = self.pool.get('product.uom') for move in self.browse(cr, uid, ids, context=context): weight = weight_net = 0.00 if move.product_id.weight > 0.00: converted_qty = move.product_qty weight = (converted_qty * move.product_id.weight) if move.product_id.weight_net > 0.00: weight_net = (converted_qty * move.product_id.weight_net) res[move.id] = { 'weight': weight, 'weight_net': weight_net, } return res _columns = { 'weight': fields.function(_cal_move_weight, type='float', string='Weight', digits_compute=dp.get_precision('Stock Weight'), multi='_cal_move_weight', store={ 'stock.move': (lambda self, cr, uid, ids, c=None: ids, ['product_id', 'product_uom_qty', 'product_uom'], 30), }), 'weight_net': fields.function(_cal_move_weight, type='float', string='Net weight', digits_compute=dp.get_precision('Stock Weight'), multi='_cal_move_weight', store={ 'stock.move': (lambda self, cr, uid, ids, c=None: ids, ['product_id', 'product_uom_qty', 'product_uom'], 30), }), 'weight_uom_id': fields.many2one( 'product.uom', 'Unit of Measure', required=True, readonly="1", help= "Unit of Measure (Unit of Measure) is the unit of measurement for Weight", ), } def action_confirm(self, cr, uid, ids, context=None): """ Pass the carrier to the picking from the sales order (Should also work in case of Phantom BoMs when on explosion the original move is deleted) """ procs_to_check = [] for move in self.browse(cr, uid, ids, context=context): if move.procurement_id and move.procurement_id.sale_line_id and move.procurement_id.sale_line_id.order_id.carrier_id: procs_to_check += [move.procurement_id] res = super(stock_move, self).action_confirm(cr, uid, ids, context=context) pick_obj = self.pool.get("stock.picking") for proc in procs_to_check: pickings = list( set([ x.picking_id.id for x in proc.move_ids if x.picking_id and not x.picking_id.carrier_id ])) if pickings: pick_obj.write( cr, uid, pickings, {'carrier_id': proc.sale_line_id.order_id.carrier_id.id}, context=context) return res def _get_default_uom(self, cr, uid, context=None): uom_categ_id = self.pool.get('ir.model.data').xmlid_to_res_id( cr, uid, 'product.product_uom_categ_kgm') return self.pool.get('product.uom').search( cr, uid, [('category_id', '=', uom_categ_id), ('factor', '=', 1)])[0] _defaults = { 'weight_uom_id': lambda self, cr, uid, c: self._get_default_uom(cr, uid, c), }
} val = val1 = 0.0 cur = order.pricelist_id.currency_id for line in order.order_line: val1 += line.price_subtotal val += self._amount_line_tax(cr, uid, line, context=context) res[order.id]['amount_tax'] = cur_obj.round(cr, uid, cur, val) res[order.id]['amount_untaxed'] = cur_obj.round(cr, uid, cur, val1) res[order.id]['amount_total'] = res[order.id]['amount_untaxed'] + res[order.id]['amount_tax'] return res _columns = { 'tot_volume': fields.function(_tot_volume, string='Total Volume', type='float'), 'avg_volume': fields.function(_avg_volume, string='Average Volume', type='float'), 'calculated' : fields.boolean('Amount Calculated'), 'amount_untaxed': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Untaxed Amount', store={ 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line', 'calculated'], 10), 'sale.order.line': (_get_order, ['purchase_price','price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10), }, multi='sums', help="The amount without tax.", track_visibility='always'), 'amount_tax': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Taxes', store={ 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 10), 'sale.order.line': (_get_order, ['purchase_price', 'price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10), }, multi='sums', help="The tax amount."), 'amount_total': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Total', store={ 'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line', 'calculated'], 10), 'sale.order.line': (_get_order, ['purchase_price', 'price_unit', 'tax_id', 'discount', 'product_uom_qty'], 10),
class stock_picking(osv.osv): _inherit = 'stock.picking' def _cal_weight(self, cr, uid, ids, name, args, context=None): res = {} for picking in self.browse(cr, uid, ids, context=context): total_weight = total_weight_net = 0.00 for move in picking.move_lines: total_weight += move.weight total_weight_net += move.weight_net res[picking.id] = { 'weight': total_weight, 'weight_net': total_weight_net, } return res def _get_picking_line(self, cr, uid, ids, context=None): result = {} for line in self.pool.get('stock.move').browse(cr, uid, ids, context=context): result[line.picking_id.id] = True return result.keys() _columns = { 'carrier_id': fields.many2one("delivery.carrier", "Carrier"), 'volume': fields.float('Volume'), 'weight': fields.function(_cal_weight, type='float', string='Weight', digits_compute=dp.get_precision('Stock Weight'), multi='_cal_weight', store={ 'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['move_lines'], 40), 'stock.move': (_get_picking_line, [ 'picking_id', 'product_id', 'product_uom_qty', 'product_uom' ], 40), }), 'weight_net': fields.function(_cal_weight, type='float', string='Net Weight', digits_compute=dp.get_precision('Stock Weight'), multi='_cal_weight', store={ 'stock.picking': (lambda self, cr, uid, ids, c={}: ids, ['move_lines'], 40), 'stock.move': (_get_picking_line, [ 'picking_id', 'product_id', 'product_uom_qty', 'product_uom' ], 40), }), 'carrier_tracking_ref': fields.char('Carrier Tracking Ref'), 'number_of_packages': fields.integer('Number of Packages'), 'weight_uom_id': fields.many2one( 'product.uom', 'Unit of Measure', required=True, readonly="1", help="Unit of measurement for Weight", ), } def _prepare_shipping_invoice_line(self, cr, uid, picking, invoice, context=None): """Prepare the invoice line to add to the shipping costs to the shipping's invoice. :param browse_record picking: the stock picking being invoiced :param browse_record invoice: the stock picking's invoice :return: dict containing the values to create the invoice line, or None to create nothing """ carrier_obj = self.pool.get('delivery.carrier') grid_obj = self.pool.get('delivery.grid') if not picking.carrier_id or \ any(inv_line.product_id.id == picking.carrier_id.product_id.id for inv_line in invoice.invoice_line): return None grid_id = carrier_obj.grid_get(cr, uid, [picking.carrier_id.id], picking.partner_id.id, context=context) if not grid_id: raise osv.except_osv(_('Warning!'), _('The carrier %s (id: %d) has no delivery grid!') \ % (picking.carrier_id.name, picking.carrier_id.id)) quantity = sum([line.product_uom_qty for line in picking.move_lines]) price = grid_obj.get_price_from_picking(cr, uid, grid_id, invoice.amount_untaxed, picking.weight, picking.volume, quantity, context=context) account_id = picking.carrier_id.product_id.property_account_income.id if not account_id: account_id = picking.carrier_id.product_id.categ_id\ .property_account_income_categ.id taxes = picking.carrier_id.product_id.taxes_id partner = picking.partner_id or False if partner: account_id = self.pool.get('account.fiscal.position').map_account( cr, uid, partner.property_account_position, account_id) taxes_ids = self.pool.get('account.fiscal.position').map_tax( cr, uid, partner.property_account_position, taxes) else: taxes_ids = [x.id for x in taxes] return { 'name': picking.carrier_id.name, 'invoice_id': invoice.id, 'uos_id': picking.carrier_id.product_id.uos_id.id, 'product_id': picking.carrier_id.product_id.id, 'account_id': account_id, 'price_unit': price, 'quantity': 1, 'invoice_line_tax_id': [(6, 0, taxes_ids)], } def _create_invoice_from_picking(self, cr, uid, picking, vals, context=None): invoice_obj = self.pool.get('account.invoice') invoice_line_obj = self.pool.get('account.invoice.line') invoice_id = super(stock_picking, self)._create_invoice_from_picking(cr, uid, picking, vals, context=context) invoice = invoice_obj.browse(cr, uid, invoice_id, context=context) invoice_line = self._prepare_shipping_invoice_line(cr, uid, picking, invoice, context=context) if invoice_line: invoice_line_obj.create(cr, uid, invoice_line) return invoice_id def _get_default_uom(self, cr, uid, context=None): uom_categ_id = self.pool.get('ir.model.data').xmlid_to_res_id( cr, uid, 'product.product_uom_categ_kgm') return self.pool.get('product.uom').search( cr, uid, [('category_id', '=', uom_categ_id), ('factor', '=', 1)])[0] _defaults = { 'weight_uom_id': lambda self, cr, uid, c: self._get_default_uom(cr, uid, c), }
import time from openerp.osv import orm, fields from openerp.addons import decimal_precision as dp from .res_company import COMPANY_FISCAL_TYPE, COMPANY_FISCAL_TYPE_DEFAULT FISCAL_RULE_COLUMNS = { 'partner_fiscal_type_id': fields.many2one( 'l10n_br_account.partner.fiscal.type', 'Tipo Fiscal do Parceiro'), 'fiscal_category_id': fields.many2one( 'l10n_br_account.fiscal.category', 'Categoria'), 'fiscal_type': fields.selection(COMPANY_FISCAL_TYPE, u'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") } OTHERS_FISCAL_RULE_COLUMNS_TEMPLATE = { 'parent_id': fields.many2one( 'account.fiscal.position.rule.template', 'Regra Pai'), 'child_ids': fields.one2many( 'account.fiscal.position.rule.template', 'parent_id', 'Regras Filhas'), } OTHERS_FISCAL_RULE_COLUMNS = {
class product_product_ext_lot(osv.Model): _inherit = 'product.product' def get_base_uom(self, cr, uid, ids, context=None): ''' Returns the UOM which is the reference for the UOM of this product. This method must receive just one ID, or a list with just one ID, because all the others will be ignored. ''' if context is None: context = {} if not isinstance(ids, list): ids = [ids] uom_obj = self.pool.get('product.uom') product = self.browse(cr, uid, ids[0], context=context) uom_ids = uom_obj.search( cr, uid, [('category_id', '=', product.uom_id.category_id.id), ('uom_type', '=', 'reference')], context=context) # If we don't find the UOM is is the base one, we will return False. base_uom = False if uom_ids: base_uom = uom_obj.browse(cr, uid, uom_ids[0], context=context) return base_uom def get_lots_available(self, cr, uid, ids, context=None): ''' Returns the list of stock.production.lots which have a use_date which is greater than today, or which is not set, for the current product. The result is sorted by 'life_date ASC, use_date ASC, date ASC' ''' if context is None: context = {} if not isinstance(ids, list): ids = ids[0] stock_production_lot_obj = self.pool.get('stock.production.lot') product = self.browse(cr, uid, ids[0], context=context) lot_ids = stock_production_lot_obj.search( cr, uid, [('product_id', '=', product.id), '|', ('use_date', '>', fields.datetime.now()), ('use_date', '=', False)], order='life_date ASC, use_date ASC, create_date ASC', context=context) lots = stock_production_lot_obj.browse(cr, uid, lot_ids, context=context) return lots def _get_lot_date(self, cr, uid, product, lot, field, context=None): lot_creation_date = lot.production_date and datetime.datetime.strptime( lot.production_date, DEFAULT_SERVER_DATETIME_FORMAT) lot_use_date = lot.use_date and datetime.datetime.strptime( lot.use_date, DEFAULT_SERVER_DATETIME_FORMAT) duration_create = getattr(product, field[0]) date_create = duration_create and lot_creation_date + datetime.timedelta( days=duration_create) or None duration_end = None if lot_use_date: if field[0] in ('alert_time', 'removal_time'): f = field[0] if f == 'removal_time': f = 'block_time' f = 'expiration_{0}'.format(f) v = float(getattr(product, f)) u = getattr(product, '{0}_uom'.format(f)) if u and v: duration_end = self._expiration_uom(cr, uid, False, u, v, context=context) date_end = duration_end and lot_use_date + duration_end or None if date_end is None or date_create is not None and date_create < date_end: return date_create else: return date_end def check_product_lot_expiry_dates(self, cr, uid, locations_to_consider=None, context=None): ''' Checks the expiration-related dates of all the lots which belong to products, only if the name of the location of the lot is within the list 'locations_to_consider'. ''' if context is None: context = {} if locations_to_consider is None: locations_to_consider = [] product_obj = self.pool.get("product.product") lot_obj = self.pool.get("stock.production.lot") revision_obj = self.pool.get("stock.production.lot.revision") issue_obj = self.pool.get("project.issue") product_ids = product_obj.search(cr, uid, [], context=context) # (product, lot, message, priority, color -1=ignore date_fields = ( ('alert_time', 'alert_date', _('This product lot is near its removal date'), 4, -1), ('removal_time', 'removal_date', _('Attention: This product lot must be removed from sell'), 3, 3), ('use_time', 'use_date', _('Warning: This product lot must not be used after this date'), 2, 2), ('life_time', 'life_date', _('Danger: This product lot has exceeded its safety life-time!'), 1, 2), ) lots_under_review = [] for product in product_obj.browse(cr, uid, product_ids, context=context): for prodlot in product.stock_prodlots: if (not prodlot.prodlot_id) or (prodlot.qty <= 0) or ( prodlot.location_id.location_id.name not in locations_to_consider): continue # For any lot under revision, we update the date fields if required lot = prodlot.prodlot_id lots_under_review.append(lot.id) logger.debug("Checking product {0}\tlot {1}".format( product.name, lot.name)) for field in date_fields: if not getattr(lot, field[1]): date = self._get_lot_date(cr, uid, product, lot, field, context=context) if date and date < datetime.datetime.today(): msg = _( 'Updating lot date {0}, with value {1}, because it has been exceeded' ).format(field[1], date) # logger.debug(msg) lot_obj.write( cr, uid, lot.id, { field[1]: date.strftime( DEFAULT_SERVER_DATETIME_FORMAT) }, context=context) lot_obj.message_post(cr, uid, lot.id, msg, context=context) for lot_id in lots_under_review: lot = lot_obj.browse(cr, uid, lot_id, context=context) for field in date_fields: has_revision = False for revision in lot.revisions: if revision.indice == field[1]: has_revision = True break if has_revision: continue date_field_value = getattr(lot, field[1]) if not date_field_value: continue date = datetime.datetime.strptime( date_field_value, DEFAULT_SERVER_DATETIME_FORMAT) if date < datetime.datetime.today(): values = { 'name': field[2], 'description': None, 'indice': field[1], 'lot_id': lot.id, } revision_obj.create(cr, uid, values, context=context) issue_ids = issue_obj.find_resource_issues( cr, uid, 'stock.production.lot', lot.id, tags=['lot', 'lot-production-date'], create=True, reopen=True, context=context) for issue in issue_obj.browse(cr, uid, issue_ids, context=context): issue.message_post(field[2]) if int(issue.priority) > field[3]: issue.write({'priority': str(field[3])}) if field[4] >= 0: issue.write({'color': field[4]}) @api.one def onchange_check_decimals(self, value, decimal_accuracy_class): return self.product_tmpl_id.onchange_check_decimals( value, decimal_accuracy_class) def _compute_packing(self, cr, uid, ids, field_name, arg, context=None): ''' Computes the 'packing' for a product. The packing is the multiplication of product's lenght X width X height X weight. ''' if context is None: context = {} res = {} for product in self.browse(cr, uid, ids, context=context): res[product. id] = product.length * product.width * product.height * product.weight return res def _virtual_stock_calculation(self, cr, uid, ids, field, arg, context=None): ''' Calculates the several measures which are used to keep track of the quantity which is available. ''' if context is None: context = {} ret = {} product_uom_obj = self.pool.get('product.uom') for product in self.browse(cr, uid, ids, context=context): qty_on_reservation_quotations = 0 for reservation in product.draft_sale_order_lines: # The lines in the sale.order store the quantity in the UOM indicated # by the sale.order. But the Quantity on Hand and all the other fields # related to quantities which are shown on the product's form are in the # unit of measure of the product. Thus, we must convert it to that UOM. qty_product_uom = product_uom_obj._compute_qty( cr, uid, reservation.product_uom.id, reservation.product_uom_qty, product.uom_id.id) qty_on_reservation_quotations += qty_product_uom ret[product.id] = { 'product_reservation_qty': qty_on_reservation_quotations, 'qty_on_sale': product.qty_available - qty_on_reservation_quotations - abs(product.outgoing_qty), } return ret def _get_last_inventory(self, cr, uid, ids, field, arg=None, context=None): res = {} line_obj = self.pool.get('stock.inventory.line') for _id in ids: res[_id] = False for line_id in line_obj.search(cr, uid, [('product_id', '=', _id), ('state', '=', 'done')], order='inventory_id DESC', limit=1): res[_id] = line_obj.read(cr, uid, line_id, ['inventory_id'], context=context)['inventory_id'] return res _columns = { # Attributes related to the features a product can have. 'weight': fields.float("Weight", digits_compute=dp.get_precision('Stock Weight')), 'length': fields.float('Length', digits_compute=dp.get_precision('Stock Length'), help='Length of the product (in centimeters)'), 'width': fields.float('Width', digits_compute=dp.get_precision('Stock Width'), help='Width of the product (in centimeters)'), 'height': fields.float('Height', digits_compute=dp.get_precision('Stock Height'), help='Height of the product (in centimeters)'), 'diameter': fields.float('Diameter', digits_compute=dp.get_precision('Stock Diameter'), help='Diameter of the product (in centimeters)'), 'packing': fields.function( _compute_packing, string='Packing', readonly=True, digits_compute=dp.get_precision('Stock Packing'), store={ 'product.product': (lambda self, cr, uid, ids, context: ids, ['length', 'width', 'height', 'weight'], 10) }, help='Length x Width x Height x Weight (gross, not net)'), 'brand': fields.char('Brand', help='The brand of the product'), 'manufacturer_website': fields.char('Manufacturer\'s Website', help='Link to the manufacturer\'s web site.'), 'stock_prodlots': fields.related('last_inventory_id', 'line_ids', type='one2many', relation='stock.inventory.line', string='Stock report by serial number', readonly=True, domain=[('product_id', '=', 'id')]), 'last_inventory_id': fields.function(_get_last_inventory, string="Last inventory", type='many2one', relation='stock.inventory', store=False), 'product_reservation_qty': fields.function( _virtual_stock_calculation, type="float", string="Reservations (on Quotations)", readonly=True, multi='_virtual_stock_calculation', help= 'It shows the amount which is on sale.order lines which are in state draft, thus corresponding to ' 'quotations which are in state draft. This is useful to keep track of units which are not available ' 'because of having been reserved for an order from the shop, for example.' ), 'qty_on_sale': fields.function( _virtual_stock_calculation, type="float", string="Quantity Available", readonly=True, multi='_virtual_stock_calculation', help= "It is computed as 'Quantity On Hand' - 'Reservations (on Quotations)' - 'Outgoing'." ), 'draft_sale_order_lines': fields.one2many('sale.order.line', 'product_id', domain=[('state', 'in', ['draft'])], string='Appearance in quotations', readonly=True), 'webshop_state': fields.selection( [('', '<not active>'), ('on_sale', 'On Sale'), ('visible', 'Not on sale, but visible'), ('not_visible', 'Not visible'), ('not_visible_conditional', 'Not visible if quantity is 0')], 'Webshop State', help='The possible states for a product in a webshop.'), # The following variable (target_state) was extracted from the # original product's life-cycle: It was found that not all the clients # needed nor wanted that module, but anyway those variables were still needed # by some of the other modules they did need; thus they was placed here # in the very-top module of the connector. 'target_state': fields.selection([('inactive', 'Inactive'), ('active', 'Active')], string="Target State", required=True), } _defaults = { 'target_state': 'inactive', 'weight': 0.00, }
# Fields for Ship From from_country = fields.Many2one('res.country','Country') from_postal_code = fields.Char('Postal Code') from_residential = fields.Boolean('Residential',default=False) # Fields for Recipient recipient_id = fields.Many2one('res.partner','Recipient Partner',help="This field is optional.If not filled you can manually set the address") to_country = fields.Many2one('res.country','Country',select=True) to_country_code = fields.Char(related="to_country.code",string='Country Code') to_postal_code = fields.Char('Postal Code') to_residential = fields.Boolean('Residential',default=False) response = fields.Text( 'Response') #International Shipment commodity_lines = fields.One2many('rate.commodity.package','request_id','Commodities') customs_value = fields.Float(compute = _compute_total_customs,string = "Total Customs Value",readonly=True,digits=dp.get_precision('Account')) customs_currency = fields.Selection(fedex_lists._fedex_currency,'Customs Currency',default="USD") B13AFilingOption = fields.Selection([('FEDEX_TO_STAMP','FedEx to Stamp'), ('FILED_ELECTRONICALLY','Filed Electronically'), ('MANUALLY_ATTACHED','Manually Attached'), ('NOT_REQUIRED','Not Required'), ('SUMMARY_REPORTING','Summary Reporting')],'B13A Filing Option',default="NOT_REQUIRED") special_services_type = fields.Selection([('COD','COD')],string = "Request Special Services") cod_collection_type = fields.Selection([('ANY','Any'), ('CASH','Cash'), ('COMPANY_CHECK','Company Check'), ('GUARANTEED_FUNDS','Guarunteed'), ('PERSONAL_CHECK','Personal Check') ],'Collection Type') cod_currency = fields.Selection(fedex_lists._fedex_currency,string = "COD Currency",default="CAD")
class account_voucher(osv.osv): _inherit = 'account.voucher' _columns = { 'customer_amount': fields.float( 'Customer Payment', help= "Specify the customer payment value. Value specified here will be used in contra entry for payment journal.", digits_compute=dp.get_precision('Account'), required=False, readonly=True, states={'draft': [('readonly', False)]}), 'supplier_amount': fields.float( 'Supplier Payment', help= "Specify the supplier payment value. Value specified here will be used in contra entry for payment journal.", digits_compute=dp.get_precision('Account'), required=False, readonly=True, states={'draft': [('readonly', False)]}), } def recompute_voucher_lines(self, cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, date, context=None): """ Returns a dict that contains new values and context @param partner_id: latest value from user input for field partner_id @param args: other arguments @param context: context arguments, like lang, time zone @return: Returns a dict which contains new values, and context """ def _remove_noise_in_o2m(): """if the line is partially reconciled, then we must pay attention to display it only once and in the good o2m. This function returns True if the line is considered as noise and should not be displayed """ if line.reconcile_partial_id: if currency_id == line.currency_id.id: if line.amount_residual_currency <= 0: return True else: if line.amount_residual <= 0: return True return False if not context.get('mode', False) == 'partner': return super(account_voucher, self).recompute_voucher_lines(cr, uid, ids, partner_id, journal_id, price, currency_id, ttype, date, context=context) if context is None: context = {} context_multi_currency = context.copy() if date: context_multi_currency.update({'date': date}) currency_pool = self.pool.get('res.currency') move_line_pool = self.pool.get('account.move.line') partner_pool = self.pool.get('res.partner') journal_pool = self.pool.get('account.journal') line_pool = self.pool.get('account.voucher.line') #set default values default = { 'value': { 'line_dr_ids': [], 'line_cr_ids': [], 'pre_line': False, }, } #drop existing lines line_ids = ids and line_pool.search( cr, uid, [('voucher_id', '=', ids[0])]) or False if line_ids: line_pool.unlink(cr, uid, line_ids) if not partner_id or not journal_id: return default journal = journal_pool.browse(cr, uid, journal_id, context=context) partner = partner_pool.browse(cr, uid, partner_id, context=context) currency_id = currency_id or journal.company_id.currency_id.id account_id = False if journal.type in ('sale', 'sale_refund'): account_id = partner.property_account_receivable.id elif journal.type in ('purchase', 'purchase_refund', 'expense'): account_id = partner.property_account_payable.id else: account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id default['value']['account_id'] = account_id if journal.type not in ('cash', 'bank'): return default total_credit = 0.0 total_debit = 0.0 account_type = 'receivable' if ttype == 'payment': account_type = 'payable' total_debit = price or 0.0 else: total_credit = price or 0.0 account_type = 'receivable' if not context.get('move_line_ids', False): ids = move_line_pool.search( cr, uid, [('state', '=', 'valid'), ('account_id.type', '=', account_type), ('reconcile_id', '=', False), ('partner_id', '=', partner_id)], context=context) else: ids = context['move_line_ids'] if context.get('mode', False) == 'partner': account_type = ('receivable', 'payable') ids = move_line_pool.search( cr, uid, [('state', '=', 'valid'), ('account_id.type', 'in', account_type), ('reconcile_id', '=', False), ('partner_id', '=', partner_id)], context=context) invoice_id = context.get('invoice_id', False) company_currency = journal.company_id.currency_id.id move_line_found = False #order the lines by most old first ids.reverse() account_move_lines = move_line_pool.browse(cr, uid, ids, context=context) #compute the total debit/credit and look for a matching open amount or invoice for line in account_move_lines: if _remove_noise_in_o2m(): continue if invoice_id: if line.invoice.id == invoice_id: #if the invoice linked to the voucher line is equal to the invoice_id in context #then we assign the amount on that line, whatever the other voucher lines move_line_found = line.id break elif currency_id == company_currency: #otherwise treatments is the same but with other field names if line.amount_residual == price: #if the amount residual is equal the amount voucher, we assign it to that voucher #line, whatever the other voucher lines move_line_found = line.id break #otherwise we will split the voucher amount on each line (by most old first) total_credit += line.credit or 0.0 total_debit += line.debit or 0.0 elif currency_id == line.currency_id.id: if line.amount_residual_currency == price: move_line_found = line.id break total_credit += line.credit and line.amount_currency or 0.0 total_debit += line.debit and line.amount_currency or 0.0 #voucher line creation for line in account_move_lines: if _remove_noise_in_o2m(): continue if line.currency_id and currency_id == line.currency_id.id: amount_original = abs(line.amount_currency) amount_unreconciled = abs(line.amount_residual_currency) else: amount_original = currency_pool.compute( cr, uid, company_currency, currency_id, line.credit or line.debit or 0.0) amount_unreconciled = currency_pool.compute( cr, uid, company_currency, currency_id, abs(line.amount_residual)) line_currency_id = line.currency_id and line.currency_id.id or company_currency rs = { 'name': line.move_id.name, 'type': line.credit and 'dr' or 'cr', 'move_line_id': line.id, 'account_id': line.account_id.id, 'amount_original': amount_original, 'amount': (move_line_found == line.id) and min(abs(price), amount_unreconciled) or 0.0, 'date_original': line.date, 'date_due': line.date_maturity, 'amount_unreconciled': amount_unreconciled, 'currency_id': line_currency_id, } #in case a corresponding move_line hasn't been found, we now try to assign the voucher amount #on existing invoices: we split voucher amount by most old first, but only for lines in the same currency if not move_line_found: if currency_id == line_currency_id: if line.credit: amount = min(amount_unreconciled, abs(total_debit)) rs['amount'] = amount total_debit -= amount else: amount = min(amount_unreconciled, abs(total_credit)) rs['amount'] = amount total_credit -= amount if rs['amount_unreconciled'] == rs['amount']: rs['reconcile'] = True if rs['type'] == 'cr': default['value']['line_cr_ids'].append(rs) else: default['value']['line_dr_ids'].append(rs) if ttype == 'payment' and len(default['value']['line_cr_ids']) > 0: default['value']['pre_line'] = 1 elif ttype == 'receipt' and len( default['value']['line_dr_ids']) > 0: default['value']['pre_line'] = 1 default['value'][ 'writeoff_amount'] = self._compute_writeoff_amount( cr, uid, default['value']['line_dr_ids'], default['value']['line_cr_ids'], price, ttype) return default def action_move_line_create(self, cr, uid, ids, context=None): ''' Confirm the vouchers given in ids and create the journal entries for each of them ''' if context is None: context = {} move_pool = self.pool.get('account.move') move_line_pool = self.pool.get('account.move.line') for voucher in self.browse(cr, uid, ids, context=context): if voucher.move_id: continue company_currency = self._get_company_currency( cr, uid, voucher.id, context) current_currency = self._get_current_currency( cr, uid, voucher.id, context) # we select the context to use accordingly if it's a multicurrency case or not context = self._sel_context(cr, uid, voucher.id, context) # But for the operations made by _convert_amount, we always need to give the date in the context ctx = context.copy() ctx.update({'date': voucher.date}) # Create the account move record. move_id = move_pool.create(cr, uid, self.account_move_get(cr, uid, voucher.id, context=context), context=context) # Get the name of the account_move just created name = move_pool.browse(cr, uid, move_id, context=context).name # Create the first line of the voucher move_line_id = move_line_pool.create( cr, uid, self.first_move_line_get(cr, uid, voucher.id, move_id, company_currency, current_currency, context), context) move_line_brw = move_line_pool.browse(cr, uid, move_line_id, context=context) line_total = move_line_brw.debit - move_line_brw.credit rec_list_ids = [] if voucher.type == 'sale': line_total = line_total - self._convert_amount( cr, uid, voucher.tax_amount, voucher.id, context=ctx) elif voucher.type == 'purchase': line_total = line_total + self._convert_amount( cr, uid, voucher.tax_amount, voucher.id, context=ctx) # Create one move line per voucher line where amount is not 0.0 line_total, rec_list_ids = self.voucher_move_line_create( cr, uid, voucher.id, line_total, move_id, company_currency, current_currency, context) # Create the writeoff line if needed ml_writeoff = self.writeoff_move_line_get(cr, uid, voucher.id, line_total, move_id, name, company_currency, current_currency, context) if ml_writeoff: move_line_pool.create(cr, uid, ml_writeoff, context) # We post the voucher. self.write(cr, uid, [voucher.id], { 'move_id': move_id, 'state': 'posted', 'number': name, }) if voucher.journal_id.entry_posted: move_pool.post(cr, uid, [move_id], context={}) # We automatically reconcile the account move lines. reconcile = False for rec_ids in rec_list_ids: if len(rec_ids) >= 2: reconcile = move_line_pool.reconcile_partial( cr, uid, rec_ids, writeoff_acc_id=voucher.writeoff_acc_id.id, writeoff_period_id=voucher.period_id.id, writeoff_journal_id=voucher.journal_id.id) return True # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
# -*- coding: utf-8 -*- import time from datetime import date from datetime import datetime from dateutil.relativedelta import relativedelta from openerp.osv import fields, osv, orm import openerp.addons.decimal_precision as dp DP = dp.get_precision('Brokers') class ResUser(orm.Model): _inherit = 'res.users' _columns = { 'contractor_id': fields.many2one( 'res.partner', string='Canal de Distribución' ) } class InsuranceParentesco(orm.Model): _name = 'insurance.parentesco' _description = 'Parentezco de Deudores' _columns = { 'name': fields.char('Parentesco', size=32, required=True), 'conyugue': fields.boolean('Conyugue ?')
class account_invoice_with_discount(models.Model): _inherit = 'account.invoice' ''' Questa funzione sostituisce quella di base per il calcolo dei totali della fattura. ''' @api.one @api.depends('invoice_line.price_subtotal', 'invoice_line.free', 'tax_line.amount', 'global_discount_lines') def _compute_amount(self): cur_obj = self.pool.get('res.currency') cur = self.currency_id tax_lines = {} #VALORIZZO I VARI RISULTATI, COME LO SAREBBERO NELLA FUNZIONE BASE self.amount_untaxed = sum(line.price_subtotal for line in self.invoice_line) self.amount_tax = sum(line.amount for line in self.tax_line) self.amount_total = self.amount_untaxed + self.amount_tax self.amount_untaxed_free = 0 self.amount_tax_free = 0 temporary_untaxed_value = 0.0 temporary_global_discount_total = 0.0 temporary_showed_global_discount_total = 0.0 original_untaxed_value = 0.0 original_total_value = 0.0 original_tax_value = 0.0 for line in self.invoice_line: original_untaxed_value = 0.0 original_total_value = 0.0 original_tax_value = 0.0 temporary_untaxed_value += line.price_subtotal ''' SE LA RIGA E' UN OMAGGIO, INCREMENTO L'IMPORTO TOTALE DEGLI OMAGGI DELL'IMPONIBILE DELLA RIGA STESSA (SENZA SCONTO) INOLTRE, SE SI TRATTA DI UN OMAGGIO CON RIVALSA, MEMORIZZO LA RIGA DI TASSE ASSOCIATA ALLA RIGA DI FATTURA, POICHE' ANCHE TALI TASSE DOVRANNO ESSERE OMAGGIATE ''' if line.free in ['gift', 'base_gift']: self.amount_untaxed_free += line.price_subtotal if line.free == 'gift': for tax in line.invoice_line_tax_id: if tax.amount in tax_lines: tax_lines[tax.amount] += line.price_subtotal else: tax_lines[tax.amount] = line.price_subtotal ''' NEL CASO IN CUI LA RIGA NON SIA UN OMAGGIO, SIA LEGATA AD UN PRODOTTO E TALE PRODOTTO AMMETTA IL CALCOLO DEGLI SCONTI GLOBALI, NE RICALCOLO L'IMPONIBILE, L'IMPORTO IN TASSE ED IL TOTALE, APPLICANDOVI SEQUENZIALMENTE TUTTI GLI SCONTI GLOBALI ''' if line.free not in [ 'gift', 'base_gift' ] and (not line.product_id or (line.product_id and not line.product_id.no_discount)): for tax in line.invoice_line_tax_id: original_tax_value += line.price_subtotal * tax.amount original_untaxed_value = line.price_subtotal original_total_value = original_untaxed_value + original_tax_value for discount in self.global_discount_lines: val = original_tax_value val1 = original_untaxed_value val2 = original_total_value if discount.type == 'fisso': temporary_global_discount_total += discount.value temporary_showed_global_discount_total += discount.value perc = discount.value / val1 else: perc = discount.value / 100 sc = val * perc val -= sc sc1 = val1 * perc val1 -= sc1 if discount.type == 'perc': temporary_global_discount_total += sc + sc1 temporary_showed_global_discount_total += sc1 original_tax_value = cur_obj.round(self._cr, self._uid, cur, val) original_untaxed_value = cur_obj.round( self._cr, self._uid, cur, val1) original_total_value = original_tax_value + original_untaxed_value #CALCOLO IL TOTALE DI TASSE OMAGGIO for tl in tax_lines: self.amount_tax_free += tax_lines[tl] * tl #CALCOLO, IN PERCENTUALE, A QUANTO AMMONTA LO SCONTO TOTALE if self.amount_total: if original_untaxed_value + temporary_showed_global_discount_total: self.global_discount_percentual = temporary_showed_global_discount_total / ( original_untaxed_value + temporary_showed_global_discount_total) else: self.global_discount_percentual = 0.0 self.showed_global_discount_total = temporary_showed_global_discount_total self.global_discount_total = temporary_global_discount_total self.amount_tax = self.amount_tax - self.amount_tax_free - ( temporary_global_discount_total - temporary_showed_global_discount_total) self.amount_untaxed = temporary_untaxed_value - temporary_showed_global_discount_total - self.amount_untaxed_free self.amount_total = self.amount_tax + self.amount_untaxed #COLUMNS global_discount_lines = fields.One2many('account.invoice.discount', 'account_id', string='Sconti Globali') global_discount_total = fields.Float(string='Totale Sconti', digits=dp.get_precision('Account'), store=True, readonly=True, compute='_compute_amount') showed_global_discount_total = fields.Float( string='Totale Sconti', digits=dp.get_precision('Account'), store=True, readonly=True, compute='_compute_amount') global_discount_percentual = fields.Float(string='Totale Sconti (%)', store=True, readonly=True, compute='_compute_amount') amount_untaxed = fields.Float(string='Subtotal', digits=dp.get_precision('Account'), store=True, readonly=True, compute='_compute_amount', track_visibility='always') amount_tax = fields.Float(string='Tax', digits=dp.get_precision('Account'), store=True, readonly=True, compute='_compute_amount') amount_total = fields.Float(string='Total', digits=dp.get_precision('Account'), store=True, readonly=True, compute='_compute_amount') ''' QUESTA FUNZIONE SI INSERISCE NEL FLUSSO DI CREAZIONE DELLE RIGHE DI MOVIMENTAZIONE CONTABILI ALLA VALIDAZIONE DI UNA FATTURA ''' @api.multi def finalize_invoice_move_lines(self, move_lines): ''' Chiamiamo la super per reperire le move_lines da creare. Tuttavia, osserviamo che gli importi di queste move_lines non sono quelli corretti, poiché già ridotti degli sconti. Noi desideriamo che le righe contabili in questa fase non siano ridotte degli sconti perché intendiamo stornare gli sconti con delle righe apposite ''' move_lines = super(account_invoice_with_discount, self).finalize_invoice_move_lines(move_lines) tax_obj = self.pool.get('account.tax') self._cr.execute( '''SELECT inv.showed_global_discount_total, inv.global_discount_total, inv.amount_total, jour.type FROM account_invoice AS inv, account_journal AS jour WHERE inv.id = %s AND jour.id = inv.journal_id''', (self.id, )) t = self._cr.fetchall()[0] showed_global_discount_total = t[0] global_discount_total = t[1] amount_total = t[2] type = t[3] #Qualora la fattura presenti degli sconti globali, bisogna rivalorizzare le righe contabili già esistenti ed eventualmente creare righe contabili di storno if showed_global_discount_total > 0.0: if not (self.env.user.company_id.discount_untaxed_account_id and self.env.user.company_id.discount_tax_account_id): raise Warning( _("No discount accounts defined for this company")) # ricalcoliamo le scadenze, poi creiamo un dizionario con chiave 'data di scadenza' e come valore la somma degli importi di tutte le righe che scadono in quella data ctx = {} for item in self._context.items(): ctx[item[0]] = item[1] if self._name == 'account.invoice': ctx['invoice_id'] = self.id totlines = self.with_context(ctx).payment_term.compute( amount_total, self.date_invoice)[0] dict = {} for tl in totlines: if tl[0] in dict: dict[tl[0]] += tl[1] else: dict[tl[0]] = tl[1] ''' Ricalcoliamo i totali di 'debito' e 'credito' delle righe, in modo che non risultino già stornate nel loro importo. Inoltre, reperiamo i dati relativi ai conti contabili da utilizzare. ''' tax_code_id = None base_code_id = None for ml in move_lines: ml_maturity = ml[2]['date_maturity'] if ml_maturity in dict: if type in ['sale', 'purchase_refund']: ml[2]['debit'] = dict[ml[2]['date_maturity']] elif type in ['purchase', 'sale_refund']: ml[2]['credit'] = dict[ml[2]['date_maturity']] if not tax_code_id: if 'tax_code_id' in ml[2] and ml[2]['tax_code_id']: tax_ids = tax_obj.search( self._cr, self._uid, [('tax_code_id', '=', ml[2]['tax_code_id'])], self._context) if tax_ids and len(tax_ids) and tax_ids[0]: tax_code_id = ml[2]['tax_code_id'] if not base_code_id: if 'tax_code_id' in ml[2] and ml[2]['tax_code_id']: tax_ids = tax_obj.search( self._cr, self._uid, [('base_code_id', '=', ml[2]['tax_code_id'])], self._context) if tax_ids and len(tax_ids) and tax_ids[0]: base_code_id = ml[2]['tax_code_id'] #Se siamo in presenza di sconti, creiamo una riga di storno imponibile if showed_global_discount_total: # riga imponibile omaggio new_line = { 'analytic_account_id': False, 'tax_code_id': base_code_id or False, 'analytic_lines': [], 'tax_amount': (type in ['sale', 'purchase_refund'] and -showed_global_discount_total) or (type in ['purchase', 'sale_refund'] and showed_global_discount_total), 'name': _('"Discount" Amount'), 'ref': '', 'currency_id': False, 'debit': type in ['sale', 'purchase_refund'] and showed_global_discount_total, 'product_id': False, 'date_maturity': False, 'credit': type in ['purchase', 'sale_refund'] and showed_global_discount_total, 'date': move_lines[0][2]['date'], 'amount_currency': 0, 'product_uom_id': False, 'quantity': 1, 'partner_id': move_lines[0][2]['partner_id'], 'account_id': (self.env.user.company_id.discount_untaxed_account_id.id), } move_lines += [(0, 0, new_line)] #Se lo sconto ha comportato anche una riduzione dell'imposta, creiamo una riga di storno imposta. if global_discount_total - showed_global_discount_total > 0.0: # riga iva omaggio # if precision_diff > 0.0: new_line = { 'analytic_account_id': False, 'tax_code_id': tax_code_id or False, 'analytic_lines': [], 'tax_amount': (type in ['sale', 'purchase_refund'] and -(global_discount_total - showed_global_discount_total)) or (type in ['purchase', 'sale_refund'] and (global_discount_total - showed_global_discount_total)), 'name': _('"Discount" Tax Amount'), 'ref': '', 'currency_id': False, 'debit': type in ['sale', 'purchase_refund'] and (global_discount_total - showed_global_discount_total), 'product_id': False, 'date_maturity': False, 'credit': type in ['purchase', 'sale_refund'] and (global_discount_total - showed_global_discount_total), 'date': move_lines[0][2]['date'], 'amount_currency': 0, 'product_uom_id': False, 'quantity': 1, 'partner_id': move_lines[0][2]['partner_id'], 'account_id': (self.env.user.company_id.discount_tax_account_id.id), } move_lines += [(0, 0, new_line)] #Reperiamo i totali di debito e credito calcolati sommando tutte le righe contabili fin qui presenti tot_debit = 0.0 tot_credit = 0.0 for line in move_lines: if line[2]['credit'] and line[2]['credit'] > 0.0: tot_credit += line[2]['credit'] if line[2]['debit'] and line[2]['debit'] > 0.0: tot_debit += line[2]['debit'] ''' Poiché potrebbero esserci errori di arrotondamento in seguito ai calcoli degli sconti, (si considera errore di arrotondamento un valore compreso tra 0 e 0.01) incrementiamo o riduciamo (a seconda che l'errore sia in eccesso o in difetto) l'importo di debito o credito dell'ultima riga contabile (a seconda che tale riga abbia valorizzata la colonna di credito o debito). ''' if abs(round(tot_debit - tot_credit, 2)) <= 0.01 and abs( round(tot_debit - tot_credit, 2)) > 0.0: prv = abs(round(tot_debit - tot_credit, 2)) if tot_debit > tot_credit: if showed_global_discount_total and global_discount_total - showed_global_discount_total > 0.0: if move_lines[-2][2]['debit']: move_lines[-2][2].update({ 'debit': move_lines[-2][2]['debit'] - round(tot_debit - tot_credit, 2) }) elif move_lines[-2][2]['credit']: move_lines[-2][2].update({ 'credit': move_lines[-2][2]['credit'] + round(tot_debit - tot_credit, 2) }) else: if move_lines[-1][2]['debit']: move_lines[-1][2].update({ 'debit': move_lines[-1][2]['debit'] - round(tot_debit - tot_credit, 2) }) elif move_lines[-1][2]['credit']: move_lines[-1][2].update({ 'credit': move_lines[-1][2]['credit'] + round(tot_debit - tot_credit, 2) }) else: if showed_global_discount_total and global_discount_total - showed_global_discount_total > 0.0: if move_lines[-2][2]['debit']: move_lines[-2][2].update({ 'debit': move_lines[-2][2]['debit'] + round(tot_credit - tot_debit, 2) }) elif move_lines[-2][2]['credit']: move_lines[-2][2].update({ 'credit': move_lines[-2][2]['credit'] - round(tot_credit - tot_debit, 2) }) else: if move_lines[-1][2]['debit']: move_lines[-1][2].update({ 'debit': move_lines[-1][2]['debit'] + round(tot_credit - tot_debit, 2) }) elif move_lines[-1][2]['credit']: move_lines[-1][2].update({ 'credit': move_lines[-1][2]['credit'] - round(tot_credit - tot_debit, 2) }) return move_lines
def compute_landed_cost(self, cr, uid, ids, context=None): line_obj = self.pool.get("stock.valuation.adjustment.lines") unlink_ids = line_obj.search(cr, uid, [("cost_id", "in", ids)], context=context) line_obj.unlink(cr, uid, unlink_ids, context=context) digits = dp.get_precision("Product Price")(cr) towrite_dict = {} for cost in self.browse(cr, uid, ids, context=None): if not cost.picking_ids: continue picking_ids = [p.id for p in cost.picking_ids] total_qty = 0.0 total_cost = 0.0 total_weight = 0.0 total_volume = 0.0 total_line = 0.0 vals = self.get_valuation_lines(cr, uid, [cost.id], picking_ids=picking_ids, context=context) for v in vals: for line in cost.cost_lines: v.update({"cost_id": cost.id, "cost_line_id": line.id}) self.pool.get("stock.valuation.adjustment.lines").create(cr, uid, v, context=context) total_qty += v.get("quantity", 0.0) total_cost += v.get("former_cost", 0.0) total_weight += v.get("weight", 0.0) total_volume += v.get("volume", 0.0) total_line += 1 for line in cost.cost_lines: value_split = 0.0 for valuation in cost.valuation_adjustment_lines: value = 0.0 if valuation.cost_line_id and valuation.cost_line_id.id == line.id: if line.split_method == "by_quantity" and total_qty: per_unit = line.price_unit / total_qty value = valuation.quantity * per_unit elif line.split_method == "by_weight" and total_weight: per_unit = line.price_unit / total_weight value = valuation.weight * per_unit elif line.split_method == "by_volume" and total_volume: per_unit = line.price_unit / total_volume value = valuation.volume * per_unit elif line.split_method == "equal": value = line.price_unit / total_line elif line.split_method == "by_current_cost_price" and total_cost: per_unit = line.price_unit / total_cost value = valuation.former_cost * per_unit else: value = line.price_unit / total_line if digits: value = float_round(value, precision_digits=digits[1], rounding_method="UP") value = min(value, line.price_unit - value_split) value_split += value if valuation.id not in towrite_dict: towrite_dict[valuation.id] = value else: towrite_dict[valuation.id] += value if towrite_dict: for key, value in towrite_dict.items(): line_obj.write(cr, uid, key, {"additional_landed_cost": value}, context=context) return True
class PurchaseRequestLine(models.Model): _name = "purchase.request.line" _description = "Purchase Request Line" _inherit = ['mail.thread', 'ir.needaction_mixin'] @api.multi @api.depends('product_id', 'name', 'product_uom_id', 'product_qty', 'analytic_account_id', 'date_required', 'specifications') def _compute_is_editable(self): for rec in self: if rec.request_id.state in ('to_approve', 'approved', 'rejected'): rec.is_editable = False else: rec.is_editable = True @api.multi def _compute_supplier_id(self): for rec in self: if rec.product_id: if rec.product_id.seller_ids: rec.supplier_id = rec.product_id.seller_ids[0].name product_id = fields.Many2one('product.product', 'Product', domain=[('purchase_ok', '=', True)], track_visibility='onchange') name = fields.Char('Description', size=256, track_visibility='onchange') product_uom_id = fields.Many2one('product.uom', 'Product Unit of Measure', track_visibility='onchange') product_qty = fields.Float( 'Quantity', track_visibility='onchange', digits_compute=dp.get_precision('Product Unit of Measure')) product_price = fields.Float('Price', track_visibility='onchange') accepted = fields.Boolean('Accepted', track_visibility='onchange') request_id = fields.Many2one('purchase.request', 'Purchase Request', ondelete='cascade', readonly=True) company_id = fields.Many2one('res.company', related='request_id.company_id', string='Company', store=True, readonly=True) analytic_account_id = fields.Many2one('account.analytic.account', 'Analytic Account', track_visibility='onchange') requested_by = fields.Many2one('res.users', related='request_id.requested_by', string='Requested by', store=True, readonly=True) assigned_to = fields.Many2one('res.users', related='request_id.assigned_to', string='Assigned to', store=True, readonly=True) date_start = fields.Date(related='request_id.date_start', string='Request Date', readonly=True, store=True) description = fields.Text(related='request_id.description', string='Description', readonly=True, store=True) origin = fields.Char(related='request_id.origin', size=32, string='Source Document', readonly=True, store=True) date_required = fields.Date(string='Request Date', required=True, track_visibility='onchange', default=fields.Date.context_today) is_editable = fields.Boolean(string='Is editable', compute="_compute_is_editable", readonly=True) specifications = fields.Text(string='Specifications') request_state = fields.Selection(string='Request state', readonly=True, related='request_id.state', selection=_STATES, store=True) supplier_id = fields.Many2one('res.partner', string='Preferred supplier', compute="_compute_supplier_id") procurement_id = fields.Many2one('procurement.order', 'Procurement Order', readonly=True) attachment_ids = fields.Many2many('ir.attachment', 'class_ir_attachments_rel', 'class_id', 'attachment_id', 'Attachments') price_total = fields.Float(string='Total', track_visibility='onchange', compute="_compute_amount", store=True) @api.onchange('product_id') def onchange_product_id(self): if self.product_id: name = self.product_id.name if self.product_id.code: name = '[%s] %s' % (name, self.product_id.code) if self.product_id.description_purchase: name += '\n' + self.product_id.description_purchase self.product_uom_id = self.product_id.uom_id.id self.product_qty = 1 self.name = name @api.multi @api.depends('product_qty', 'product_price') def _compute_amount(self): if self.product_qty > 0: self.price_total = self.product_price * self.product_qty
"sequence": fields.integer("Sequence"), "product_id": fields.many2one( "product.product", "Product", domain=[("is_rent", "=", True)], change_default=True ), "invoice_lines": fields.many2many( "account.invoice.line", "rent_order_line_invoice_rel", "order_line_id", "invoice_id", "Invoice Lines", readonly=True, ), "price_unit": fields.float( "Unit Price", required=True, digits_compute=dp.get_precision("Product Price"), readonly=True, states={"draft": [("readonly", False)]}, ), "price_subtotal": fields.function(_amount_line, string="Subtotal", digits_compute=dp.get_precision("Account")), "tax_id": fields.many2many( "account.tax", "rent_order_tax", "order_line_id", "tax_id", "Taxes", readonly=True, states={"draft": [("readonly", False)]}, ), "product_uom_qty": fields.float( "Quantity",
class wh_move_line(models.Model): _name = 'wh.move.line' _rec_name = 'note' MOVE_LINE_TYPE = [ ('out', u'出库'), ('in', u'入库'), ('internal', u'内部调拨'), ] MOVE_LINE_STATE = [ ('draft', u'草稿'), ('done', u'已审核'), ] ORIGIN_EXPLAIN = { ('wh.assembly', 'out'): u'组装单子件', ('wh.assembly', 'in'): u'组装单组合件', ('wh.disassembly', 'out'): u'拆卸单组合件', ('wh.disassembly', 'in'): u'拆卸单子件', ('wh.internal', True): u'调拨出库', ('wh.internal', False): u'调拨入库', 'wh.out.inventory': u'盘亏', 'wh.out.others': u'其他出库', 'wh.in.inventory': u'盘盈', 'wh.in.others': u'其他入库', 'buy.receipt.sell': u'采购入库', 'buy.receipt.return': u'采购退货', 'sell.delivery.sell': u'销售出库', 'sell.delivery.return': u'销售退货', } @api.one @api.depends('goods_qty', 'price_taxed', 'discount_amount', 'tax_rate') def _compute_all_amount(self): '''当订单行的数量、含税单价、折扣额、税率改变时,改变金额、税额、价税合计''' self.price = self.price_taxed / (1 + self.tax_rate * 0.01) self.amount = self.goods_qty * self.price - self.discount_amount # 折扣后金额 self.tax_amount = self.amount * self.tax_rate * 0.01 # 税额 self.subtotal = self.amount + self.tax_amount @api.one @api.depends('goods_id') def _compute_using_attribute(self): self.using_attribute = self.goods_id.attribute_ids and True or False @api.one @api.depends('move_id.warehouse_id') def _get_line_warehouse(self): self.warehouse_id = self.move_id.warehouse_id.id if (self.move_id.origin == 'wh.assembly' or self.move_id.origin == 'wh.disassembly') and self.type == 'in': self.warehouse_id = self.env.ref( 'warehouse.warehouse_production').id @api.one @api.depends('move_id.warehouse_dest_id') def _get_line_warehouse_dest(self): self.warehouse_dest_id = self.move_id.warehouse_dest_id.id if (self.move_id.origin == 'wh.assembly' or self.move_id.origin == 'wh.disassembly') and self.type == 'out': self.warehouse_dest_id = self.env.ref( 'warehouse.warehouse_production').id move_id = fields.Many2one('wh.move', string=u'移库单', ondelete='cascade', help=u'出库/入库/移库单行对应的移库单') date = fields.Date(u'完成日期', copy=False, help=u'单据完成日期') cost_time = fields.Datetime(u'审核时间', copy=False, help=u'单据审核时间') type = fields.Selection(MOVE_LINE_TYPE, u'类型', default=lambda self: self.env.context.get('type'), help=u'类型:出库、入库 或者 内部调拨') state = fields.Selection(MOVE_LINE_STATE, u'状态', copy=False, default='draft', help=u'状态标识,新建时状态为草稿;审核后状态为已审核') goods_id = fields.Many2one('goods', string=u'产品', required=True, index=True, ondelete='restrict', help=u'该单据行对应的产品') using_attribute = fields.Boolean(compute='_compute_using_attribute', string=u'使用属性', help=u'该单据行对应的产品是否存在属性,存在True否则False') attribute_id = fields.Many2one('attribute', u'属性', ondelete='restrict', help=u'该单据行对应的产品的属性') using_batch = fields.Boolean(related='goods_id.using_batch', string=u'批号管理', help=u'该单据行对应的产品是否使用批号管理') force_batch_one = fields.Boolean(related='goods_id.force_batch_one', string=u'每批号数量为1', help=u'该单据行对应的产品是否每批号数量为1,是True否则False') lot = fields.Char(u'批号', help=u'该单据行对应的产品的批号,一般是入库单行') lot_id = fields.Many2one('wh.move.line', u'批号', help=u'该单据行对应的产品的批号,一般是出库单行') lot_qty = fields.Float(related='lot_id.qty_remaining', string=u'批号数量', digits=dp.get_precision('Quantity'), help=u'该单据行对应的产品批号的商品剩余数量') lot_uos_qty = fields.Float(u'批号辅助数量', digits=dp.get_precision('Quantity'), help=u'该单据行对应的产品的批号辅助数量') production_date = fields.Date(u'生产日期', default=fields.Date.context_today, help=u'产品的生产日期') shelf_life = fields.Integer(u'保质期(天)', help=u'产品的保质期(天)') valid_date = fields.Date(u'有效期至', help=u'产品的有效期') uom_id = fields.Many2one('uom', string=u'单位', ondelete='restrict', help=u'产品的计量单位') uos_id = fields.Many2one('uom', string=u'辅助单位', ondelete='restrict', help=u'产品的辅助单位') warehouse_id = fields.Many2one('warehouse', u'调出仓库', ondelete='restrict', store=True, compute=_get_line_warehouse, help=u'单据的来源仓库') warehouse_dest_id = fields.Many2one('warehouse', u'调入仓库', ondelete='restrict', store=True, compute=_get_line_warehouse_dest, help=u'单据的目的仓库') goods_qty = fields.Float(u'数量', digits=dp.get_precision('Quantity'), default=1, help=u'产品的数量') goods_uos_qty = fields.Float(u'辅助数量', digits=dp.get_precision('Quantity'), default=1, help=u'产品的辅助数量') price = fields.Float(u'单价', compute=_compute_all_amount, store=True, readonly=True, digits=dp.get_precision('Amount'), help=u'产品的单价') price_taxed = fields.Float(u'含税单价', digits=dp.get_precision('Amount'), help=u'产品的含税单价') discount_rate = fields.Float(u'折扣率%', help=u'单据的折扣率%') discount_amount = fields.Float(u'折扣额', digits=dp.get_precision('Amount'), help=u'单据的折扣额') amount = fields.Float(u'金额', compute=_compute_all_amount, store=True, readonly=True, digits=dp.get_precision('Amount'), help=u'单据的金额,计算得来') tax_rate = fields.Float(u'税率(%)', help=u'单据的税率(%)') tax_amount = fields.Float(u'税额', compute=_compute_all_amount, store=True, readonly=True, digits=dp.get_precision('Amount'), help=u'单据的税额,有单价×数量×税率计算得来') subtotal = fields.Float(u'价税合计', compute=_compute_all_amount, store=True, readonly=True, digits=dp.get_precision('Amount'), help=u'价税合计,有不含税金额+税额计算得来') note = fields.Text(u'备注', help=u'可以为该单据添加一些需要的标识信息') cost_unit = fields.Float(u'单位成本', digits=dp.get_precision('Amount'), help=u'入库/出库单位成本') cost = fields.Float(u'成本', compute='_compute_cost', inverse='_inverse_cost', digits=dp.get_precision('Amount'), store=True, help=u'入库/出库成本') @api.one @api.depends('cost_unit', 'goods_qty') def _compute_cost(self): self.cost = self.cost_unit * self.goods_qty @api.one def _inverse_cost(self): self.cost_unit = safe_division(self.cost, self.goods_qty) def get_origin_explain(self): self.ensure_one() if self.move_id.origin in ('wh.assembly', 'wh.disassembly'): return self.ORIGIN_EXPLAIN.get((self.move_id.origin, self.type)) elif self.move_id.origin == 'wh.internal': return self.ORIGIN_EXPLAIN.get( (self.move_id.origin, self.env.context.get('internal_out', False))) elif self.move_id.origin in self.ORIGIN_EXPLAIN.keys(): return self.ORIGIN_EXPLAIN.get(self.move_id.origin) return '' @api.model def default_get(self, fields): res = super(wh_move_line, self).default_get(fields) if self.env.context.get('goods_id') and self.env.context.get( 'warehouse_id'): res.update({ 'goods_id': self.env.context.get('goods_id'), 'warehouse_id': self.env.context.get('warehouse_id') }) return res def get_real_cost_unit(self): self.ensure_one() return safe_division(self.cost, self.goods_qty) @api.multi def name_get(self): res = [] for line in self: if self.env.context.get('match'): res.append((line.id, '%s-%s->%s(%s, %s%s)' % (line.move_id.name, line.warehouse_id.name, line.warehouse_dest_id.name, line.goods_id.name, str(line.goods_qty), line.uom_id.name))) else: res.append((line.id, line.lot)) return res @api.model def name_search(self, name='', args=None, operator='ilike', limit=100): ''' 批号下拉的时候显示批次和剩余数量 ''' result = [] domain = [] if args: domain = args if name: domain.append(('lot', operator, name)) records = self.search(domain, limit=limit) for line in records: result.append( (line.id, u'%s %s 余 %s' % (line.lot, line.warehouse_dest_id.name, line.qty_remaining))) return result def check_availability(self): if self.warehouse_dest_id == self.warehouse_id: raise osv.except_osv(u'错误', u'调出仓库不可以和调入仓库一样') def prev_action_done(self): pass @api.multi def action_done(self): for line in self: line.check_availability() line.prev_action_done() line.write({ 'state': 'done', 'date': line.move_id.date, 'cost_time': fields.Datetime.now(self), }) def check_cancel(self): pass def prev_action_cancel(self): pass @api.multi def action_cancel(self): for line in self: line.check_cancel() line.prev_action_cancel() line.write({ 'state': 'draft', 'date': False, }) @api.one def compute_lot_compatible(self): if self.warehouse_id and self.lot_id and self.lot_id.warehouse_dest_id != self.warehouse_id: self.lot_id = False if self.goods_id and self.lot_id and self.lot_id.goods_id != self.goods_id: self.lot_id = False def compute_lot_domain(self): lot_domain = [('goods_id', '=', self.goods_id.id), ('state', '=', 'done'), ('lot', '!=', False), ('qty_remaining', '>', 0)] if self.move_id: lot_domain.append( ('warehouse_dest_id', '=', self.move_id.warehouse_id.id)) if self.attribute_id: lot_domain.append(('attribute_id', '=', self.attribute_id.id)) return lot_domain @api.one def compute_suggested_cost(self): if self.env.context.get( 'type' ) == 'out' and self.goods_id and self.warehouse_id and self.goods_qty: cost, cost_unit = self.goods_id.get_suggested_cost_by_warehouse( self.warehouse_id, self.goods_qty, self.lot_id, self.attribute_id) self.cost_unit = cost_unit self.cost = cost @api.multi @api.onchange('goods_id') def onchange_goods_id(self): if self.goods_id: self.uom_id = self.goods_id.uom_id self.uos_id = self.goods_id.uos_id self.attribute_id = False self.cost_unit = self.goods_id.cost if self.goods_id.using_batch and self.goods_id.force_batch_one: self.goods_qty = 1 self.goods_uos_qty = self.goods_id.anti_conversion_unit( self.goods_qty) else: self.goods_qty = self.goods_id.conversion_unit( self.goods_uos_qty or 1) self.compute_suggested_cost() self.compute_lot_compatible() return {'domain': {'lot_id': self.compute_lot_domain()}} @api.multi @api.onchange('warehouse_id') def onchange_warehouse_id(self): self.compute_suggested_cost() self.compute_lot_domain() self.compute_lot_compatible() return {'domain': {'lot_id': self.compute_lot_domain()}} @api.multi @api.onchange('attribute_id') def onchange_attribute_id(self): self.compute_suggested_cost() return {'domain': {'lot_id': self.compute_lot_domain()}} @api.one @api.onchange('goods_qty') def onchange_goods_qty(self): self.compute_suggested_cost() @api.one @api.onchange('goods_uos_qty') def onchange_goods_uos_qty(self): if self.goods_id: self.goods_qty = self.goods_id.conversion_unit(self.goods_uos_qty) self.compute_suggested_cost() @api.one @api.onchange('lot_id') def onchange_lot_id(self): if self.lot_id: self.lot_qty = self.lot_id.qty_remaining self.lot_uos_qty = self.goods_id.anti_conversion_unit(self.lot_qty) if self.env.context.get('type') == 'internal': self.lot = self.lot_id.lot @api.one @api.onchange('goods_qty', 'price_taxed', 'discount_rate') def onchange_discount_rate(self): '''当数量、单价或优惠率发生变化时,优惠金额发生变化''' price = self.price_taxed / (1 + self.tax_rate * 0.01) self.discount_amount = self.goods_qty * price * self.discount_rate * 0.01 @api.multi def unlink(self): for line in self: if line.state == 'done': raise osv.except_osv(u'错误', u'不可以删除已经完成的明细') return super(wh_move_line, self).unlink()
def compute_landed_cost(self, cr, uid, ids, context=None): line_obj = self.pool.get('stock.valuation.adjustment.lines') unlink_ids = line_obj.search(cr, uid, [('cost_id', 'in', ids)], context=context) line_obj.unlink(cr, uid, unlink_ids, context=context) digits = dp.get_precision('Product Price')(cr) towrite_dict = {} for cost in self.browse(cr, uid, ids, context=None): if not cost.picking_ids: continue picking_ids = [p.id for p in cost.picking_ids] total_qty = 0.0 total_cost = 0.0 total_weight = 0.0 total_volume = 0.0 total_line = 0.0 vals = self.get_valuation_lines(cr, uid, [cost.id], picking_ids=picking_ids, context=context) for v in vals: for line in cost.cost_lines: v.update({'cost_id': cost.id, 'cost_line_id': line.id}) self.pool.get('stock.valuation.adjustment.lines').create(cr, uid, v, context=context) total_qty += v.get('quantity', 0.0) total_cost += v.get('former_cost', 0.0) total_weight += v.get('weight', 0.0) total_volume += v.get('volume', 0.0) total_line += 1 for line in cost.cost_lines: value_split = 0.0 for valuation in cost.valuation_adjustment_lines: value = 0.0 if valuation.cost_line_id and valuation.cost_line_id.id == line.id: if line.split_method == 'by_quantity' and total_qty: per_unit = (line.price_unit / total_qty) value = valuation.quantity * per_unit elif line.split_method == 'by_weight' and total_weight: per_unit = (line.price_unit / total_weight) value = valuation.weight * per_unit elif line.split_method == 'by_volume' and total_volume: per_unit = (line.price_unit / total_volume) value = valuation.volume * per_unit elif line.split_method == 'equal': value = (line.price_unit / total_line) elif line.split_method == 'by_current_cost_price' and total_cost: per_unit = (line.price_unit / total_cost) value = valuation.former_cost * per_unit else: value = (line.price_unit / total_line) if digits: value = float_round(value, precision_digits=digits[1], rounding_method='UP') fnc = min if line.price_unit > 0 else max value = fnc(value, line.price_unit - value_split) value_split += value if valuation.id not in towrite_dict: towrite_dict[valuation.id] = value else: towrite_dict[valuation.id] += value if towrite_dict: for key, value in towrite_dict.items(): line_obj.write(cr, uid, key, {'additional_landed_cost': value}, context=context) return True
def _get_uom_id(self, cr, uid, context=None): try: proxy = self.pool.get('ir.model.data') result = proxy.get_object_reference(cr, uid, 'product', 'product_uom_unit') return result[1] except Exception, ex: return False _columns = { # 'location_destination_id': fields.many2one('stock.location', 'Stock Destination Location'), # 'location_id': fields.many2one('stock.location', 'Stock Source Location'), 'product_id': fields.many2one('product.product', 'Product'), 'back_order_id': fields.many2one('back.to.back.order', 'Back Order'), 'qty': fields.float('Quantity'), 'price': fields.float('Unit Price'), 'subtotal': fields.function(_amount_line, string='Subtotal', digits_compute= dp.get_precision('Account')), 'taxes_id': fields.many2many('account.tax', 'purchase_order_taxe', 'ord_id', 'tax_id', 'Taxes'), 'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True), } _defaults = { 'product_uom' : _get_uom_id, } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: