class FleetVehicleLogFuel(models.Model): _name = 'fleet.vehicle.log.fuel' _inherit = ['fleet.vehicle.log.fuel', 'mail.thread', 'ir.needaction_mixin'] _order = "date desc,vehicle_id desc" name = fields.Char() travel_id = fields.Many2one( 'tms.travel', string='Travel',) expense_id = fields.Many2one( 'tms.expense', string='Expense',) employee_id = fields.Many2one( 'hr.employee', string='Driver', domain=[('driver', '=', True)], compute='_compute_employee_id', store=True,) odometer = fields.Float(related='vehicle_id.odometer',) product_uom_id = fields.Many2one( 'product.uom', string='UoM ') product_qty = fields.Float( string='Liters', required=True, default=1.0,) tax_amount = fields.Float( string='Taxes', required=True,) price_total = fields.Float( string='Total', required=True,) special_tax_amount = fields.Float( compute="_compute_special_tax_amount", string='IEPS',) price_unit = fields.Float( compute='_compute_price_unit', string='Unit Price',) price_subtotal = fields.Float( string="Subtotal", compute='_compute_price_subtotal',) invoice_id = fields.Many2one( 'account.invoice', 'Invoice', readonly=True, domain=[('state', '!=', 'cancel')],) invoice_paid = fields.Boolean( compute='_compute_invoiced_paid') operating_unit_id = fields.Many2one( 'operating.unit', string='Operating Unit',) currency_id = fields.Many2one( 'res.currency', string='Currency', required=True, default=lambda self: self.env.user.company_id.currency_id,) notes = fields.Char() state = fields.Selection( [('draft', 'Draft'), ('approved', 'Approved'), ('confirmed', 'Confirmed'), ('closed', 'Closed'), ('cancel', 'Cancelled')], readonly=True, default='draft',) no_travel = fields.Boolean( help="Check this if you want to create Fuel Voucher " "with no Travel.",) vendor_id = fields.Many2one( 'res.partner', required=True) product_id = fields.Many2one( 'product.product', string='Product', required=True, domain=[('tms_product_category', '=', 'fuel')]) currency_id = fields.Many2one( 'res.currency', 'Currency', required=True, default=lambda self: self.env.user.company_id.currency_id) expense_control = fields.Boolean( readonly=True, ) ticket_number = fields.Char() @api.multi @api.depends('vehicle_id') def _compute_employee_id(self): for rec in self: rec.employee_id = rec.vehicle_id.employee_id.id @api.multi @api.depends('tax_amount') def _compute_price_subtotal(self): for rec in self: rec.price_subtotal = 0 if rec.tax_amount > 0: rec.price_subtotal = rec.tax_amount / 0.16 @api.multi @api.depends('product_qty', 'price_subtotal') def _compute_price_unit(self): for rec in self: rec.price_unit = 0 if rec.product_qty and rec.price_subtotal > 0: rec.price_unit = rec.price_subtotal / rec.product_qty @api.multi @api.depends('price_subtotal', 'tax_amount', 'price_total') def _compute_special_tax_amount(self): for rec in self: rec.special_tax_amount = 0 if rec.price_subtotal and rec.price_total and rec.tax_amount > 0: rec.special_tax_amount = ( rec.price_total - rec.price_subtotal - rec.tax_amount) @api.multi def action_approved(self): for rec in self: message = _('<b>Fuel Voucher Approved.</b></br><ul>' '<li><b>Approved by: </b>%s</li>' '<li><b>Approved at: </b>%s</li>' '</ul>') % (self.env.user.name, fields.Date.today()) rec.message_post(body=message) rec.state = 'approved' @api.multi def action_cancel(self): for rec in self: if rec.invoice_id: raise ValidationError( _('Could not cancel Fuel Voucher !'), _('This Fuel Voucher is already Invoiced')) elif (rec.travel_id and rec.travel_id.state == 'closed'): raise ValidationError( _('Could not cancel Fuel Voucher !' 'This Fuel Voucher is already linked to Travel ' 'Expenses record')) @api.model def create(self, values): res = super(FleetVehicleLogFuel, self).create(values) if not res.operating_unit_id.fuel_log_sequence_id: raise ValidationError(_( 'You need to define the sequence for fuel logs in base %s' % res.operating_unit_id.name )) sequence = res.operating_unit_id.fuel_log_sequence_id res.name = sequence.next_by_id() return res @api.multi def set_2_draft(self): for rec in self: message = _( '<b>Fuel Voucher Draft.</b></br><ul>' '<li><b>Drafted by: </b>%s</li>' '<li><b>Drafted at: </b>%s</li>' '</ul>') % (self.env.user.name, fields.Date.today()) rec.message_post(body=message) rec.state = 'draft' @api.multi def action_confirm(self): for rec in self: if (rec.product_qty <= 0 or rec.tax_amount <= 0 or rec.price_total <= 0): raise ValidationError( _('Liters, Taxes and Total' ' must be greater than zero.')) message = _( '<b>Fuel Voucher Confirmed.</b></br><ul>' '<li><b>Confirmed by: </b>%s</li>' '<li><b>Confirmed at: </b>%s</li>' '</ul>') % (self.env.user.name, fields.Date.today()) rec.message_post(body=message) rec.state = 'confirmed' @api.onchange('travel_id') def _onchange_travel(self): self.vehicle_id = self.travel_id.unit_id self.employee_id = self.travel_id.employee_id @api.depends('invoice_id') def _compute_invoiced_paid(self): for rec in self: rec.invoice_paid = ( rec.invoice_id.id and rec.invoice_id.state == 'paid') @api.onchange('no_travel') def _onchange_no_tracel(self): if self.no_travel: self.travel_id = False self.vehicle_id = False @api.multi def _amount_to_text(self, product_qty): total = str(float(product_qty)).split('.')[0] total = num2words(float(total), lang='es').upper() return '%s' % (total)
class mrp_workcenter_product_cost(models.Model): _name = 'mrp.workcenter.product.cost' _description = 'Per-product workcenter cost' @api.model def _default_cost_concepts(self): mrp_wc_cost_concept_obj = self.env['mrp.workcenter.cost.concept'] all_concept_names = mrp_wc_cost_concept_obj.search_read([], ['name']) lines = [] for x in all_concept_names: lines.append((0, 0, {'name': x['name']})) return lines @api.multi def _get_cost_all(self): res = {} precision = self.env['decimal.precision'].precision_get( 'Product costing') for record in self: res[record.id] = { 'cost_hour': 0.0, 'cost_cycle': 0.0, 'cost_uom': 0.0, } for line in record.line_ids: record.cost_hour += line.cost record.cost_cycle = round( record.cost_hour * \ record.time_cycle, precision) record.cost_uom = round( record.cost_cycle / \ (record.capacity_per_cycle * \ record.product_efficiency), precision) workcenter_id = fields.Many2one(comodel_name="mrp.workcenter", string="Workcenter", required=True) # TODO: Filtro para que sólo se muestren productos que usen esta máquina. product_id = fields.Many2one(comodel_name="product.product", string="Product", required=True) product_tmpl_id = fields.Many2one(comodel_name="product.template", string="Product template", related="product_id.product_tmpl_id") sequence = fields.Integer(string="Sequence", required=True, default=10) capacity_per_cycle = fields.Float(string="Capacity/cycle", required=True, help="Quantity of production product (in product's UoM) " "that the workcenter contributes to produce in a cycle.", digits=(4, 5), default=1.0) time_cycle = fields.Float(string="Cycle time", help="Time needed for this machine to complete" " one cycle.", digits=(4, 9)) product_efficiency = fields.Float(string="Efficiency factor", help="A factor of 0.9 means a loss of 10% during the production " "process.", required=True, default=1.0) cost_hour = fields.Float(string="Cost per hour", digits=dp.get_precision('Product costing'), help="Calculated cost per hour.", compute=_get_cost_all) cost_cycle = fields.Float(string="Cost per cycle", digits=dp.get_precision('Product costing'), help="Calculated cost per cycle.", compute=_get_cost_all) cost_uom = fields.Float(string="Cost per UoM", digits=dp.get_precision('Product costing'), help="Calculated cost per product's UoM.", compute=_get_cost_all) line_ids = fields.One2many(comodel_name="mrp.workcenter.product.cost.item", inverse_name="workcenter_product_cost_id", string="Cost details", default=_default_cost_concepts ) @api.one @api.constrains('capacity_per_cycle') def _check_qty(self): if self.capacity_per_cycle <= 0: raise ValidationError("Capacity per cycle cannot " "be negative or zero!") return True # Do not allow to declare several cost lines for the same product on a # given workcenter _sql_constraints = [ ('product_uniq', 'unique(workcenter_id, product_id)', _("A product's cost definition must be unique per work center!")), ]
class ProductUomTotal(models.Model): _inherit = 'product.uom.total' dim_quantity = fields.Float(string='Nombre', digits_compute=dp.get_precision('Product UoS'))
class stock_count(models.Model): _name = 'stock.count' _description = 'statistics stock inventory,On Hand ,Draft,Commit, \ Backorder,Available,On Order' product_id = fields.Many2one('product.product', 'Product') location_id = fields.Many2one('stock.location', 'Inventory Location') report_date = fields.Datetime('Report Date') on_hand_qty = fields.Float('ON hand') draft_qty = fields.Float('Draft') commit_qty = fields.Float('commit') backorder_qty = fields.Float('Backorder') available_qty = fields.Float('Available') on_order_qty = fields.Float('On Order') on_sale_qty = fields.Float('On Sale') _order = "product_id,location_id" @api.multi def compute_stock_report(self, product_ids=[], location_ids=[]): if not product_ids: product_ids = [ x.id for x in self.env['product.product'].search([('active', '=', True)]) ] if not location_ids: location_ids = [ x.id for x in self.env['stock.location'].search([('is_compute', '=', True)]) ] self.delete_stock_count_old_data(product_ids, location_ids) vals = self.prepare_stock_report_data(product_ids, location_ids) for val in vals: self.create(val) def delete_stock_count_old_data(self, product_ids=[], location_ids=[]): count_ids = self.search([('product_id', 'in', product_ids), ('location_id', 'in', location_ids)]) if count_ids: count_ids.unlink() def prepare_stock_report_data(self, product_ids=[], location_ids=[]): products = self.env['product.product'].browse(product_ids) draft_vals = self.prepare_draft_qty() vals = [] for location in self.env['stock.location'].browse(location_ids): qtys = {} qtys = self.pool.get('product.product')._product_available( self.env.cr, self.env.uid, product_ids, context={'location': location.id}) for product in products: product_qtys = qtys.get(product.id, {}) outgoing_qty = product_qtys.get('outgoing_qty', 0) on_hand_qty = product_qtys.get('qty_available', 0) val_line = { 'product_id': product.id, 'location_id': location.id, 'report_date': datetime.datetime.today(), 'draft_qty': 0, 'on_hand_qty': on_hand_qty, 'commit_qty': self.prepare_commit(outgoing_qty, on_hand_qty), 'backorder_qty': self.prepare_backorder(outgoing_qty, on_hand_qty), 'available_qty': self.prepare_available(outgoing_qty, on_hand_qty), 'on_order_qty': product_qtys.get('incoming_qty', 0), 'on_sale_qty': outgoing_qty } if location.is_draft_location: val_line.update( {'draft_qty': draft_vals.get(product.id, 0)}) vals.append(val_line) return vals def get_value_from_params(self): return def prepare_commit(self, sale_qty, on_hand_qty): return min(sale_qty, on_hand_qty) def prepare_backorder(self, sale_qty, on_hand_qty): if sale_qty >= on_hand_qty: return (sale_qty - on_hand_qty) return 0 def prepare_available(self, sale_qty, on_hand_qty): if on_hand_qty > sale_qty: return on_hand_qty - sale_qty return 0 def prepare_draft_qty(self): sale_orde_obj = self.env['sale.order'] sale_order_draft_ids = [ x.id for x in sale_orde_obj.search([('is_draft', '=', 'True'), ('state', '=', 'draft')]) ] order_line_obj = self.env['sale.order.line'] count_res = order_line_obj.read_group( [['order_id', 'in', sale_order_draft_ids]], ['product_uom_qty', 'product_id'], ['product_id']) vals = {} for count in count_res: if count['product_id']: product_id = count['product_id'][0] draft_qty = count['product_uom_qty'] vals[product_id] = draft_qty return vals
class CashFlowReport(models.TransientModel): _name = 'cash.flow' _description = u'Relatório de Fluxo de Caixa' @api.one def calc_final_amount(self): balance = 0 for line in self.line_ids: balance += line.amount balance += self.start_amount self.final_amount = balance name = fields.Char(string="Descrição", required=True) start_date = fields.Date(string="Data Inicio", required=True, default=fields.date.today()) end_date = fields.Date(string="Data Fim", required=True, default=fields.date.today() + datetime.timedelta(6 * 365 / 12)) start_amount = fields.Float(string="Valor Inicial", digits_compute=dp.get_precision('Account')) final_amount = fields.Float(string="Valor Final", compute="calc_final_amount", digits_compute=dp.get_precision('Account')) line_ids = fields.One2many("cash.flow.line", "cashflow_id", string="Linhas Fluxo de Caixa") @api.multi def calculate_liquidity(self): accs = self.env['account.account'].search([('type', '=', 'liquidity')]) liquidity_lines = [] for acc in accs: if acc.balance != 0: liquidity_lines.append({ 'name': acc.name, 'cashflow_id': self.id, 'account_id': acc.id, 'debit': acc.credit, 'credit': acc.debit, 'amount': acc.balance, }) return liquidity_lines @api.multi def calculate_moves(self): moveline_obj = self.env['account.move.line'] moveline_ids = moveline_obj.search([ '|', ('account_id.type', '=', 'receivable'), ('account_id.type', '=', 'payable'), ('reconcile_id', '=', False), ('state', '!=', 'draft'), ('company_id', '=', self.env.user.company_id.id), ('date_maturity', '<=', self.end_date), ]) moves = [] for move in moveline_ids: debit, credit = move.credit, move.debit amount = move.debit - move.credit if move.reconcile_partial_id: move_ids = moveline_obj.search([ ('reconcile_partial_id', '=', move.reconcile_partial_id.id) ]) debit = sum(line.credit for line in move_ids) credit = sum(line.debit for line in move_ids) amount = credit - debit moves.append({ 'name': move.ref, 'cashflow_id': self.id, 'partner_id': move.partner_id.id, 'journal_id': move.journal_id.id, 'account_id': move.account_id.id, 'date': move.date_maturity, 'debit': debit, 'credit': credit, 'amount': amount, }) return moves @api.multi def button_calculate(self): self.write({'line_ids': [(5, 0, 0)]}) balance = self.start_amount liquidity_lines = self.calculate_liquidity() move_lines = self.calculate_moves() move_lines.sort( key=lambda x: datetime.datetime.strptime(x['date'], '%Y-%m-%d')) for lines in liquidity_lines + move_lines: balance += lines['credit'] - lines['debit'] lines['balance'] = balance self.env['cash.flow.line'].create(lines)
class jmdventa(models.Model): _name = "ae.venta" _inherit = "mail.thread" def create_move(self, cr, uid, ids, args, context=None): ret = {} for i in self.browse(cr, uid, ids, context): if i.poliza_generada: return ret #creando la poliza #print("Por crear la poliza") #poliza_obj = self.pool.get("account.move") #idpoliza = poliza_obj.create(cr, uid, { # 'name': i.concepto_poliza, # 'journal_id': i.diario.id, # }, context) #print("Poliza Creada") #linea_obj = self.pool.get("account.move.line") #linea_obj.create(cr, uid, { # 'name': i.concepto_poliza, # 'account_id': i.cuenta_cargo.id, # 'debit': i.monto, # 'move_id': idpoliza, # }, context) #print("Poliza de Cargo Creada") #linea_obj.create(cr, uid, { # 'name': i.concepto_poliza, # 'account_id': i.cuenta_abono.id, # 'credit': i.monto, # 'move_id': idpoliza, # }, context) #self.write(cr, uid, [i.id], { # 'poliza_generada': "Si" # }) return ret def create_invoice(self, cr, uid, ids, args, context=None): ret = {} for i in self.browse(cr, uid, ids, context): if i.factura: return ret invoice_obj = self.pool.get("account.invoice") idfactura = invoice_obj.create(cr, uid, { 'name': i.concepto_factura, 'partner_id': i.coproductor.id, 'account_id': i.cuenta_factura.id, }) self.write(cr, uid, [i.id], { 'factura': idfactura }) return ret name = fields.Char("Nombre") ventana = fields.Many2one("ae.ventana", "Ventana") territorio = fields.Many2one("ae.territorio", "Territorio") inicio = fields.Date("Inicio") fin = fields.Date("Fin") monto = fields.Float("Monto") tipo = fields.Selection([('capital', 'Capital'), ('especie', 'Especie')], "Tipo de contribución") proyecto = fields.Many2one("ae.proyecto", "Proyecto") coproductor = fields.Many2one("res.partner", string="Cliente") cuenta_cargo = fields.Many2one("account.account", string="Cuenta de Cargo") cuenta_abono = fields.Many2one("account.account", string="Cuenta Abono") concepto_poliza = fields.Char("Concepto de la Póliza") poliza_generada = fields.Char("Poliza Generada") diario = fields.Many2one("account.journal", string="Diario") factura = fields.One2many("account.invoice", "venta_id", string="Factura") concepto_factura = fields.Char("Concepto de la Factura") cuenta_factura = fields.Many2one("account.account", string="Cuenta de la\ Factura")
class ComputedPurchaseOrder(models.Model): _description = 'Computed Purchase Order' _name = 'computed.purchase.order' _order = 'id desc' # Constant Values _DEFAULT_NAME = _('New') _STATE = [ ('draft', 'Draft'), ('done', 'Done'), ('canceled', 'Canceled'), ] _TARGET_TYPE = [ ('product_price_inv_eq', '€'), ('time', 'days'), ('weight', 'kg'), ] _VALID_PSI = [ ('first', 'Consider only the first supplier on the product'), ('all', 'Consider all the suppliers registered on the product'), ] # Columns section name = fields.Char( 'Computed Purchase Order Reference', size=64, required=True, readonly=True, default=_DEFAULT_NAME, help="""Unique number of the automated purchase order, computed""" """ automatically when the computed purchase order is created.""") company_id = fields.Many2one( 'res.company', 'Company', readonly=True, required=True, help="""When you will validate this item, this will create a""" """ purchase order for this company.""", default=lambda self: self.env.user.company_id, ) active = fields.Boolean( 'Active', default=True, help="""By unchecking the active field, you may hide this item""" """ without deleting it.""") state = fields.Selection(_STATE, 'State', required=True, default='draft') incoming_date = fields.Date('Wished Incoming Date', help="Wished date for products delivery.") partner_id = fields.Many2one('res.partner', 'Supplier', required=True, domain=[('supplier', '=', True)], help="Supplier of the purchase order.") line_ids = fields.One2many(comodel_name='computed.purchase.order.line', inverse_name='computed_purchase_order_id', string='Order Lines', help="Products to order.") # this is to be able to display the line_ids on 2 tabs of the view stock_line_ids = fields.One2many( compute='_get_stock_line_ids', comodel_name='computed.purchase.order.line', inverse_name='computed_purchase_order_id', help="Products to order.") compute_pending_quantity = fields.Boolean( 'Pending quantity taken in account', default=True) purchase_order_id = fields.Many2one('purchase.order', 'Purchase Order', readonly=True) purchase_target = fields.Integer('Purchase Target', default=0) target_type = fields.Selection( _TARGET_TYPE, 'Target Type', required=True, default='product_price_inv_eq', help="""This defines the amount of products you want to""" """ purchase. \n""" """The system will compute a purchase order based on the stock""" """ you have and the average consumption of each product.""" """ * Target type '€': computed purchase order will cost at""" """ least the amount specified\n""" """ * Target type 'days': computed purchase order will last""" """ at least the number of days specified (according to current""" """ average consumption)\n""" """ * Target type 'kg': computed purchase order will weight at""" """ least the weight specified""") valid_psi = fields.Selection(_VALID_PSI, 'Supplier choice', required=True, default='first', help="""Method of selection of suppliers""") computed_amount = fields.Float( compute='_get_computed_amount_duration', digits_compute=dp.get_precision('Product Price'), string='Amount of the computed order', multi='computed_amount_duration') computed_duration = fields.Integer(compute='_get_computed_amount_duration', string='Minimum duration after order', multi='computed_amount_duration') products_updated = fields.Boolean( compute='_get_products_updated', string='Indicate if there were any products updated in the list') # Fields Function section @api.onchange('line_ids') @api.multi def _get_stock_line_ids(self): for spo in self: self.stock_line_ids = self.line_ids @api.multi def _get_computed_amount_duration(self): for cpo in self: min_duration = 999 amount = 0 for line in cpo.line_ids: if line.average_consumption != 0: duration = (line.computed_qty + line.purchase_qty)\ / line.average_consumption min_duration = min(duration, min_duration) amount += line.subtotal cpo.computed_amount = amount cpo.computed_duration = min_duration @api.multi def _get_products_updated(self): for cpo in self: updated = False for line in cpo.line_ids: if line.state == 'updated': updated = True break cpo.products_updated = updated # View Section @api.onchange('partner_id') def onchange_partner_id(self): # TODO: create a wizard to validate the change self.purchase_target = 0 self.target_type = 'product_price_inv_eq' if self.partner_id: self.purchase_target = self.partner_id.purchase_target self.target_type = self.partner_id.target_type self.line_ids = map(lambda x: (2, x.id, False), self.line_ids) # Overload Section @api.model def create(self, vals): if vals.get('name', self._DEFAULT_NAME) == self._DEFAULT_NAME: vals['name'] = self.env['ir.sequence'].get( 'computed.purchase.order') or '/' order = super(ComputedPurchaseOrder, self).create(vals) return order @api.multi def write(self, vals): cpo_id = super(ComputedPurchaseOrder, self).write(vals) if self.update_sorting(vals): self._sort_lines() return cpo_id @api.multi def update_sorting(self, vals): for cpo in self: try: line_ids = vals.get('line_ids', False) if not line_ids: return False # this context check will allow you to change the field list # without overriding the whole function need_sorting_fields = self.env.context.get( 'need_sorting_fields', False) if not need_sorting_fields: need_sorting_fields = [ 'average_consumption', 'computed_qty', 'stock_duration', 'manual_input_output_qty', ] for value in line_ids: if len(value) > 2 and value[2] and isinstance( value[2], dict) and (set(need_sorting_fields) & set(value[2].keys())): return True return False except: return False # Private Section @api.multi def _sort_lines(self): cpol_obj = self.env['computed.purchase.order.line'] for cpo in self: lines = cpol_obj.browse([x.id for x in cpo.line_ids]).read( ['stock_duration', 'average_consumption']) lines = sorted(lines, key=lambda line: line['average_consumption'], reverse=True) lines = sorted(lines, key=lambda line: line['stock_duration']) id_index_list = {} for i in lines: id_index_list[i['id']] = lines.index(i) for line_id in id_index_list.keys(): cpol_obj.browse(line_id).write( {'sequence': id_index_list[line_id]}) @api.model def _make_po_lines(self): all_lines = [] for line in self.line_ids: if line.purchase_qty != 0: line_values = { 'name': "%s%s" % (line.product_code_inv and '[' + line.product_code_inv + '] ' or '', line.product_name_inv or line.product_id.name_template), 'product_qty': line.purchase_qty, 'package_qty': line.package_qty, 'product_qty_package': (line.purchase_qty / line.package_qty), 'price_policy': line.price_policy, 'date_planned': (self.incoming_date or fields.Date.context_today(self)), 'product_uom': line.product_id.uom_po_id.id, 'product_id': line.product_id.id, 'price_unit': line.product_price_inv, 'discount': line.discount_inv, 'taxes_id': [(6, 0, [x.id for x in line.product_id.supplier_taxes_id])], } all_lines.append((0, 0, line_values), ) return all_lines @api.multi def _compute_purchase_quantities_days(self): for cpo in self: days = cpo.purchase_target for line in cpo.line_ids: if line.average_consumption: quantity = max( days * line.average_consumption * line.uom_po_id.factor / line.uom_id.factor - line.computed_qty, 0) if line.package_qty \ and quantity % line.package_qty: quantity = ceil(quantity / line.package_qty) *\ line.package_qty elif line.computed_qty == 0: quantity = line.package_qty or 0 else: quantity = 0 line.purchase_qty = quantity line.purchase_qty_package = quantity / line.package_qty @api.multi def _compute_purchase_quantities_other(self, field): for cpo in self: cpol_obj = self.env['computed.purchase.order.line'] if not cpo.line_ids: return False target = cpo.purchase_target ok = False days = -1 field_list = cpol_obj.browse([x.id for x in cpo.line_ids]).read([field]) field_list_dict = {} for i in field_list: field_list_dict[i['id']] = i[field] while not ok: days += 1 qty_tmp = {} for line in cpo.line_ids: if line.average_consumption: quantity = max( days * line.average_consumption * line.uom_po_id.factor / line.uom_id.factor - line.computed_qty, 0) if line.package_qty and\ quantity % line.package_qty: quantity = ceil(quantity / line.package_qty)\ * line.package_qty elif line.computed_qty == 0: quantity = line.package_qty or 0 else: quantity = 0 qty_tmp[line.id] = quantity ok = cpo._check_purchase_qty(target, field_list_dict, qty_tmp) for line in cpo.line_ids: line.purchase_qty = qty_tmp[line.id] line.purchase_qty_package = qty_tmp[line.id] / line.package_qty @api.model def _check_purchase_qty(self, target=0, field_list=None, qty_tmp=None): if not target or field_list is None or qty_tmp is None: return True total = 0 for key in field_list.keys(): total += field_list[key] * qty_tmp[key] return total >= target # Action section @api.multi def compute_active_product_stock(self): psi_obj = self.env['product.supplierinfo'] for cpo in self: cpol_list = [] # TMP delete all rows, # TODO : depends on further request to avoid user data to be lost cpo.line_ids.unlink() # Get product_product and compute stock for psi in psi_obj.search([('name', '=', cpo.partner_id.id)]): for pp in psi.product_tmpl_id.filtered( lambda pt: pt.state not in ('end', 'obsolete')).product_variant_ids: valid_psi = pp._valid_psi(cpo.valid_psi) if valid_psi and psi in valid_psi[0]: cpol_list.append((0, 0, { 'product_id': pp.id, 'state': 'up_to_date', 'product_code': psi.product_code, 'product_name': psi.product_name, 'product_price': psi.base_price, 'price_policy': psi.price_policy, 'package_qty': psi.package_qty or psi.min_qty, 'displayed_average_consumption': pp.displayed_average_consumption, 'consumption_range': pp.display_range, 'uom_po_id': psi.product_uom.id, })) # update line_ids self.line_ids = cpol_list @api.multi def compute_purchase_quantities(self): for cpo in self: if any([line.average_consumption for line in cpo.line_ids]): if cpo.target_type == 'time': return cpo._compute_purchase_quantities_days() else: return cpo._compute_purchase_quantities_other( field=cpo.target_type) @api.multi def make_order(self): for cpo in self: po_lines = cpo._make_po_lines() if not po_lines: raise ValidationError( _('All purchase quantities are set to 0!')) po_obj = self.env['purchase.order'] po_values = { 'origin': cpo.name, 'partner_id': cpo.partner_id.id, 'location_id': self.env['res.users'].browse(self.env.uid).company_id. partner_id.property_stock_customer.id, 'order_line': po_lines, 'date_planned': (cpo.incoming_date or fields.Date.context_today(self)), } po_id = po_obj.create(po_values) cpo.state = 'done' cpo.purchase_order_id = po_id mod_obj = self.env['ir.model.data'] res = mod_obj.get_object_reference('purchase', 'purchase_order_form') res_id = res and res[1] or False return { 'name': _('Purchase Order'), 'view_type': 'form', 'view_mode': 'form', 'views': [(res_id, 'form')], 'view_id': [res_id], 'res_model': 'purchase.order', 'type': 'ir.actions.act_window', 'nodestroy': True, 'target': 'current', 'res_id': po_id.id or False, }
class IndividualProgram(models.Model): '''Individual Program''' _inherit = 'school.individual_program' state = fields.Selection( [ ('draft', 'Draft'), ('progress', 'In Progress'), ('awarded', 'Awarded'), ('abandonned', 'Abandonned'), ], string='Status', index=True, default='draft', copy=False, help= " * The 'Draft' status is used when results are not confirmed yet.\n" " * The 'In Progress' status is used during the cycle.\n" " * The 'Awarded' status is used when the cycle is awarded.\n" " * The 'Abandonned' status is used if a student leave the program.\n", track_visibility='onchange') abandonned_date = fields.Date('Abandonned Date') @api.multi def set_to_draft(self, context): # TODO use a workflow to make sure only valid changes are used. return self.write({'state': 'draft'}) @api.multi def set_to_progress(self, context): # TODO use a workflow to make sure only valid changes are used. return self.write({'state': 'progress'}) @api.multi def set_to_awarded(self, context, grade_year_id=None, grade=None, grade_comments=None): # TODO use a workflow to make sure only valid changes are used. if (grade): self.write({ 'state': 'awarded', 'grade': grade, 'grade_year_id': grade_year_id, 'grade_comments': grade_comments, 'graduation_date': fields.Date.today(), }) else: self.write({ 'state': 'awarded', 'grade_year_id': grade_year_id, 'graduation_date': fields.Date.today(), }) @api.multi def set_to_abandonned(self, context): # TODO use a workflow to make sure only valid changes are used. return self.write({ 'state': 'abandonned', 'abandonned_date': fields.Date.today() }) historical_bloc_1_eval = fields.Float( string="Hist Bloc 1 Eval", track_visibility='onchange', digits=dp.get_precision('Evaluation')) historical_bloc_1_credits = fields.Integer(string="Hist Bloc 1 ECTS", track_visibility='onchange') historical_bloc_2_eval = fields.Float( string="Hist Bloc 2 Eval", track_visibility='onchange', digits=dp.get_precision('Evaluation')) historical_bloc_2_credits = fields.Integer(string="Hist Bloc 2 ECTS", track_visibility='onchange') grade = fields.Selection([ ('without', 'Without Grade'), ('satisfaction', 'Satisfaction'), ('distinction', 'Distinction'), ('second_class', 'Second Class Honor'), ('first_class', 'First Class Honor'), ], string="Grade", track_visibility='onchange') grade_year_id = fields.Many2one('school.year', string="Graduation year", track_visibility='onchange') graduation_date = fields.Date(string="Graduation Date", track_visibility='onchange') grade_comments = fields.Text(string="Grade Comments", track_visibility='onchange') evaluation = fields.Float(string="Evaluation", compute="compute_evaluation", digits=dp.get_precision('Evaluation')) total_registered_credits = fields.Integer( compute='_get_total_acquiered_credits', string='Registered Credits', track_visibility='onchange') total_acquiered_credits = fields.Integer( compute='_get_total_acquiered_credits', string='Acquiered Credits', store=True, track_visibility='onchange') program_completed = fields.Boolean(compute='_get_total_acquiered_credits', string="Program Completed", store=True, track_visibility='onchange') @api.depends('required_credits', 'bloc_ids.state', 'bloc_ids.total_acquiered_credits', 'historical_bloc_1_credits', 'historical_bloc_2_credits') @api.one def _get_total_acquiered_credits(self): _logger.debug('Trigger "_get_total_acquiered_credits" on Program %s' % self.name) total = sum( bloc_id.total_acquiered_credits if bloc_id.state in ['awarded_first_session', 'awarded_second_session', 'failed' ] else 0 for bloc_id in self.bloc_ids) total_current = sum(bloc_id.total_credits if bloc_id.state in ['progress', 'postponed'] else 0 for bloc_id in self.bloc_ids) self.total_acquiered_credits = total + self.historical_bloc_1_credits + self.historical_bloc_2_credits self.program_completed = self.total_acquiered_credits >= self.required_credits # TODO : Fix this it does not seem right @api.depends('grade') def _onchange_grade(self): if self.grade: graduation_date = fields.Date.today() self.total_registered_credits = self.total_acquiered_credits + total_current self.program_completed = self.required_credits > 0 and self.total_acquiered_credits >= self.required_credits @api.depends('bloc_ids.evaluation', 'historical_bloc_1_eval', 'historical_bloc_2_eval') @api.one def compute_evaluation(self): total = 0 count = 0 for bloc in self.bloc_ids: if bloc.evaluation > 0: # if all is granted do not count total += bloc.evaluation count += 1 if self.historical_bloc_1_eval > 0: total += self.historical_bloc_1_eval count += 1 if self.historical_bloc_2_eval > 0: total += self.historical_bloc_2_eval count += 1 if count > 0: self.evaluation = total / count # TODO : Implement computation based on UE as per the decret @api.depends('bloc_ids.evaluation', 'historical_bloc_1_eval', 'historical_bloc_2_eval') @api.multi def compute_evaluation_details(self): self.ensure_one() ret = [0, 0, 0, 0, 0, 0] if self.historical_bloc_1_eval > 0: ret[0] = self.historical_bloc_1_eval if self.historical_bloc_2_eval > 0: ret[1] = self.historical_bloc_2_eval for bloc in self.bloc_ids: ret[int(bloc.source_bloc_level) - 1] = bloc.evaluation return {'bloc_evaluations': ret}
class IndividualCourseGroup(models.Model): '''Individual Course Group''' _inherit = 'school.individual_course_group' ## Compute dispensed hours and ECTS total_dispensed_credits = fields.Integer(compute="compute_credits", string="Dispensed Credits", store=True) total_dispensed_hours = fields.Integer(compute="compute_credits", string="Dispensed Hours", store=True) total_not_dispensed_credits = fields.Integer( compute="compute_credits", string="Not Dispensed Credits", store=True) total_not_dispensed_hours = fields.Integer(compute='compute_credits', string='Not Dispensed Hours', store=True) @api.depends('course_ids.dispense') @api.one def compute_credits(self): self.total_dispensed_credits = sum( [ic.credits for ic in self.course_ids if ic.dispense]) self.total_dispensed_hours = sum( [ic.hours for ic in self.course_ids if ic.dispense]) self.total_not_dispensed_credits = self.total_credits - self.total_dispensed_credits self.total_not_dispensed_hours = self.total_hours - self.total_dispensed_hours # Actions @api.one def set_deliberated_to_ten(self, session=1, message=''): if session == 1: self.write({ 'first_session_deliberated_result': max(self.first_session_computed_result, 10), 'first_session_deliberated_result_bool': True, 'first_session_note': message, }) else: self.write({ 'second_session_deliberated_result': max(self.second_session_computed_result, 10) if self.second_session_computed_result_bool else max( self.first_session_computed_result, 10), 'second_session_deliberated_result_bool': True, 'second_session_note': message, }) state = fields.Selection( [ ('draft', 'Draft'), ('progress', 'In Progress'), ('finish', 'Awarded'), ], string='Status', index=True, readonly=True, default='draft', #track_visibility='onchange', TODO : is this useful for this case ? copy=False, help= " * The 'Draft' status is used when results are not confirmed yet.\n" " * The 'Confirmed' status is when restults are confirmed.") ## If set a course with an evaluation < 10 will make this course group not acquiered. enable_exclusion_bool = fields.Boolean( string='Enable exclusion evaluation', related="source_course_group_id.enable_exclusion_bool", readonly=True) ## First Session ## first_session_computed_result = fields.Float( compute='compute_average_results', string='First Session Computed Result', store=True, digits=dp.get_precision('Evaluation')) first_session_computed_result_bool = fields.Boolean( compute='compute_average_results', string='First Session Computed Active', store=True) first_session_computed_exclusion_result_bool = fields.Boolean( compute='compute_average_results', string='First Session Exclusion Result', store=True) first_session_deliberated_result = fields.Char( string='First Session Deliberated Result', track_visibility='onchange') first_session_deliberated_result_bool = fields.Boolean( string='First Session Deliberated Active', track_visibility='onchange') first_session_result = fields.Float( compute='compute_first_session_results', string='First Session Result', store=True, digits=dp.get_precision('Evaluation')) first_session_result_bool = fields.Boolean( compute='compute_first_session_results', string='First Session Active', store=True) first_session_acquiered = fields.Selection( ([('A', 'Acquired'), ('NA', 'Not Acquired')]), compute='compute_first_session_acquiered', string='First Session Acquired Credits', default='NA', store=True, required=True, track_visibility='onchange') first_session_note = fields.Text(string='First Session Notes') ## Second Session ## second_session_computed_result = fields.Float( compute='compute_average_results', string='Second Session Computed Result', store=True, digits=dp.get_precision('Evaluation')) second_session_computed_result_bool = fields.Boolean( compute='compute_average_results', string='Second Session Computed Active', store=True) second_session_computed_exclusion_result_bool = fields.Boolean( compute='compute_average_results', string='Second Session Exclusion Result', store=True) second_session_deliberated_result = fields.Char( string='Second Session Deliberated Result', digits=(5, 2), track_visibility='onchange') second_session_deliberated_result_bool = fields.Boolean( string='Second Session Deliberated Active', track_visibility='onchange') second_session_result = fields.Float( compute='compute_second_session_results', string='Second Session Result', store=True, digits=dp.get_precision('Evaluation')) second_session_result_bool = fields.Boolean( compute='compute_second_session_results', string='Second Session Active', store=True) second_session_acquiered = fields.Selection( ([('A', 'Acquired'), ('NA', 'Not Acquired')]), compute='compute_second_session_acquiered', string='Second Session Acquired Credits', default='NA', store=True, required=True, track_visibility='onchange') second_session_note = fields.Text(string='Second Session Notes') ## Final ## dispense = fields.Boolean(compute='compute_dispense', string='Dispense', default=False, track_visibility='onchange', store=True) final_result = fields.Float(compute='compute_final_results', string='Final Result', store=True, digits=dp.get_precision('Evaluation'), track_visibility='onchange') final_result_disp = fields.Char(string='Final Result Display', compute='compute_results_disp') final_result_bool = fields.Boolean(compute='compute_final_results', string='Final Active') acquiered = fields.Selection(([('A', 'Acquiered'), ('NA', 'Not Acquiered')]), compute='compute_acquiered', string='Acquired Credits', store=True, track_visibility='onchange', default='NA') final_note = fields.Text(string='Final Notes') @api.one def compute_results_disp(self): if not self.final_result_bool: self.final_result_disp = "" if self.dispense: self.final_result_disp = "Val" else: self.final_result_disp = "%.2f" % self.final_result def _parse_result(self, input): f = float(input) if (f < 0 or f > 20): raise ValidationError("Evaluation shall be between 0 and 20") else: return f ## override so that courses with dispense and no deferred results are excluded from computation @api.depends('course_ids.hours', 'course_ids.credits', 'course_ids.c_weight') @api.one def _get_courses_total(self): _logger.debug('Trigger "_get_courses_total" on Course Group %s' % self.name) self.total_hours = sum(course.hours for course in self.course_ids) self.total_credits = sum(course.credits for course in self.course_ids) self.total_weight = sum(course.c_weight for course in self.course_ids) @api.depends('course_ids.first_session_result_bool', 'course_ids.first_session_result', 'course_ids.second_session_result_bool', 'course_ids.second_session_result', 'course_ids.c_weight', 'course_ids.weight') @api.one def compute_average_results(self): _logger.debug('Trigger "compute_average_results" on Course Group %s' % self.name) ## Compute Weighted Average running_first_session_result = 0 running_second_session_result = 0 self.first_session_computed_result_bool = False self.first_session_computed_exclusion_result_bool = False self.second_session_computed_result_bool = False self.second_session_computed_exclusion_result_bool = False for ic in self.course_ids: # Compute First Session if ic.first_session_result_bool: running_first_session_result += ic.first_session_result * ic.c_weight self.first_session_computed_result_bool = True if ic.first_session_result < 10: self.first_session_computed_exclusion_result_bool = True # Compute Second Session if ic.second_session_result_bool: running_second_session_result += ic.second_session_result * ic.c_weight self.second_session_computed_result_bool = True if ic.second_session_result < 10: self.second_session_computed_exclusion_result_bool = True elif ic.first_session_result_bool: # Use First session in computation of the second one if no second one running_second_session_result += ic.first_session_result * ic.c_weight if ic.first_session_result < 10: self.second_session_computed_exclusion_result_bool = True if self.first_session_computed_result_bool: if self.total_weight > 0: self.first_session_computed_result = running_first_session_result / self.total_weight if self.second_session_computed_result_bool: if self.total_weight > 0: self.second_session_computed_result = running_second_session_result / self.total_weight @api.depends('first_session_deliberated_result_bool', 'first_session_deliberated_result', 'first_session_computed_result_bool', 'first_session_computed_result') @api.one def compute_first_session_results(self): _logger.debug( 'Trigger "compute_first_session_results" on Course Group %s' % self.name) ## Compute Session Results if self.first_session_deliberated_result_bool: try: f = self._parse_result(self.first_session_deliberated_result) except ValueError: self.write('first_session_deliberated_result', None) raise UserError( _('Cannot decode %s, please encode a Float eg "12.00".' % self.first_session_deliberated_result)) #if (f < self.first_session_computed_result): # # TODO : take care of this - removed due to Cours artistiques B - Art dramatique - 2 - 2015-2016 - VALERIO Maddy # # raise ValidationError("Deliberated result must be above computed result, i.e. %s > %s." % (self.first_session_deliberated_result, self.first_session_computed_result)) self.first_session_result = f #else: # self.first_session_result = f self.first_session_result_bool = True elif self.first_session_computed_result_bool: self.first_session_result = self.first_session_computed_result self.first_session_result_bool = True else: self.first_session_result = 0 self.first_session_result_bool = False @api.depends('first_session_result_bool', 'first_session_result', 'first_session_computed_exclusion_result_bool') @api.one def compute_first_session_acquiered(self): _logger.debug( 'Trigger "compute_first_session_acquiered" on Course Group %s' % self.name) self.first_session_acquiered = 'NA' #if self.first_session_deliberated_result_bool: # self.first_session_acquiered = 'A' #el if self.enable_exclusion_bool: if self.first_session_result >= 10 and ( not self.first_session_computed_exclusion_result_bool or self.first_session_deliberated_result_bool): self.first_session_acquiered = 'A' else: if self.first_session_result >= 10: # cfr appel Ingisi 27-06 and (not self.first_session_computed_exclusion_result_bool or self.first_session_deliberated_result_bool): self.first_session_acquiered = 'A' @api.depends('second_session_deliberated_result_bool', 'second_session_deliberated_result', 'second_session_computed_result_bool', 'second_session_computed_result') @api.one def compute_second_session_results(self): _logger.debug( 'Trigger "compute_second_session_results" on Course Group %s' % self.name) if self.second_session_deliberated_result_bool: try: f = self._parse_result(self.second_session_deliberated_result) except ValueError: self.write('second_session_deliberated_result', None) raise UserError( _('Cannot decode %s, please encode a Float eg "12.00".' % self.second_session_deliberated_result)) if (f < self.second_session_computed_result): raise ValidationError( "Deliberated result must be above computed result, i.e. %s > %s." % (self.second_session_deliberated_result, self.second_session_computed_result)) else: self.second_session_result = f self.second_session_result_bool = True elif self.second_session_computed_result_bool: self.second_session_result = self.second_session_computed_result self.second_session_result_bool = True else: self.second_session_result = 0 self.second_session_result_bool = False @api.depends('second_session_result_bool', 'second_session_result', 'first_session_acquiered') @api.one def compute_second_session_acquiered(self): _logger.debug( 'Trigger "compute_second_session_acquiered" on Course Group %s' % self.name) self.second_session_acquiered = self.first_session_acquiered if self.first_session_deliberated_result_bool: self.first_session_acquiered = 'A' elif self.enable_exclusion_bool: if self.second_session_result >= 10 and ( not self.second_session_computed_exclusion_result_bool or self.second_session_deliberated_result_bool): self.second_session_acquiered = 'A' else: if self.second_session_result >= 10: # and (not self.second_session_computed_exclusion_result_bool or self.second_session_deliberated_result_bool): self.second_session_acquiered = 'A' @api.depends('first_session_result', 'first_session_result_bool', 'first_session_acquiered', 'second_session_result', 'second_session_result_bool', 'second_session_acquiered') @api.one def compute_final_results(self): _logger.debug('Trigger "compute_final_results" on Course Group %s' % self.name) ## Compute Final Results if self.second_session_result_bool: self.final_result = self.second_session_result self.final_result_bool = True elif self.first_session_result_bool: self.final_result = self.first_session_result self.final_result_bool = True else: self.final_result_bool = False @api.depends('course_ids.dispense') @api.one def compute_dispense(self): # Check if Course Group is dispensed all_dispensed = True for ic in self.course_ids: all_dispensed = all_dispensed and ic.dispense if all_dispensed: self.dispense = True self.acquiered = 'A' @api.depends('dispense', 'second_session_acquiered') @api.one def compute_acquiered(self): if self.dispense: self.acquiered = 'A' else: self.acquiered = self.second_session_acquiered
class TaskTimeAccountLine(models.Model): _name = 'task.time.account.line' @api.multi def _get_default_start_time(self): active_model = self.env.context.get('active_model') if active_model =='project.task': active_id = self.env.context.get('active_id') if active_id: task_search = self.env['project.task'].search([('id','=',active_id)],limit=1) return task_search.start_time @api.multi def _get_default_end_time(self): active_model = self.env.context.get('active_model') if active_model =='project.task': active_id = self.env.context.get('active_id') if active_id: task_search = self.env['project.task'].search([('id','=',active_id)],limit=1) return task_search.end_time @api.multi def _get_default_duration(self): active_model = self.env.context.get('active_model') if active_model =='project.task': active_id = self.env.context.get('active_id') if active_id: task_search = self.env['project.task'].search([('id','=',active_id)],limit=1) return float(task_search.total_time) name = fields.Char("Description",required = True) start_date = fields.Datetime("Start Date", default = _get_default_start_time , readonly=True) end_date = fields.Datetime("End Date",default = _get_default_end_time , readonly=True) duration = fields.Float("Duration (HH:MM)",default = _get_default_duration , readonly=True) @api.multi def end_task(self): context = dict(self.env.context or {}) active_model = context.get('active_model',False) active_id = context.get('active_id',False) vals = {'name':self.name,'unit_amount':self.duration,'amount':self.duration,'date': datetime.now()} if active_model=='project.task': if active_id: task_search =self.env['project.task'].search([('id','=',active_id)],limit=1) if task_search: vals.update({'start_date':task_search.start_time }) vals.update({'end_date':task_search.end_time }) vals.update({'is_timesheet':True }) if task_search.project_id: project_id = task_search.project_id.id if project_id: act_id = self.env['project.project'].sudo().browse(task_search.project_id.id).analytic_account_id if act_id : vals.update({'account_id':act_id.id}) else : analytic_account_search = self.env.user.company_id.analytic_account_id if analytic_account_search: if analytic_account_search.id: vals.update({'account_id':analytic_account_search.id }) else : raise UserError('Please set Default Analytic Account for Timesheet, from Project -> Settings') task_search.sudo().write({'start_time':None}) usr_id = context.get('uid',False) if usr_id: vals.update({'user_id': usr_id }) line_obj = self.env['account.analytic.line'].sudo().create(vals) self.sudo()._cr.commit() return
class IndividualBloc(models.Model): '''Individual Bloc''' _inherit = 'school.individual_bloc' state = fields.Selection( [ ('draft', 'Draft'), ('progress', 'In Progress'), ('postponed', 'Postponed'), ('awarded_first_session', 'Awarded in First Session'), ('awarded_second_session', 'Awarded in Second Session'), ('failed', 'Failed'), ('abandoned', 'Abandoned'), ], string='Status', index=True, default='draft', copy=False, help= " * The 'Draft' status is used when results are not confirmed yet.\n" " * The 'In Progress' status is used during the courses.\n" " * The 'Postponed' status is used when a second session is required.\n" " * The 'Awarded' status is used when the bloc is awarded in either first or second session.\n" " * The 'Failed' status is used when the bloc is definitively considered as failed.\n" " * The 'Abandoned' status is when the student abandoned his bloc.\n", track_visibility='onchange') total_acquiered_credits = fields.Integer(compute="compute_credits", string="Acquiered Credits", store=True) total_acquiered_hours = fields.Integer(compute="compute_credits", string="Acquiered Hours", store=True) total_not_acquiered_credits = fields.Integer( compute='compute_credits', string='Not Acquiered Credits', store=True) total_not_acquiered_hours = fields.Integer(compute='compute_credits', string='Not Acquiered Hours', store=True) total_dispensed_credits = fields.Integer(compute="compute_credits", string="Dispensed Credits", store=True) total_dispensed_hours = fields.Integer(compute="compute_credits", string="Dispensed Hours", store=True) total_not_dispensed_credits = fields.Integer( compute="compute_credits", string="Not Dispensed Credits", store=True) total_not_dispensed_hours = fields.Integer(compute='compute_credits', string='Not Dispensed Hours', store=True) evaluation = fields.Float(string="Evaluation", compute="compute_evaluation", digits=dp.get_precision('Evaluation')) decision = fields.Text(string="Decision", track_visibility='onchange') exclude_from_deliberation = fields.Boolean( string='Exclude from Deliberation', default=False) first_session_result = fields.Float(string="Evaluation", compute="compute_evaluation", digits=dp.get_precision('Evaluation')) second_session_result = fields.Float(string="Evaluation", compute="compute_evaluation", digits=dp.get_precision('Evaluation')) @api.multi def set_to_draft(self, context): # TODO use a workflow to make sure only valid changes are used. return self.write({'state': 'draft'}) @api.multi def set_to_progress(self, context): # TODO use a workflow to make sure only valid changes are used. return self.write({'state': 'progress'}) @api.multi def set_to_postponed(self, decision=None, context=None): # TODO use a workflow to make sure only valid changes are used. if isinstance(decision, dict): context = decision decision = None return self.write({'state': 'postponed', 'decision': decision}) @api.multi def set_to_awarded_first_session(self, decision=None, context=None): # TODO use a workflow to make sure only valid changes are used. if isinstance(decision, dict): context = decision decision = None return self.write({ 'state': 'awarded_first_session', 'decision': decision }) @api.multi def set_to_awarded_second_session(self, decision=None, context=None): # TODO use a workflow to make sure only valid changes are used. if isinstance(decision, dict): context = decision decision = None return self.write({ 'state': 'awarded_second_session', 'decision': decision }) @api.multi def set_to_failed(self, decision=None, context=None): # TODO use a workflow to make sure only valid changes are used. if isinstance(decision, dict): context = decision decision = None return self.write({'state': 'failed', 'decision': decision}) @api.multi def set_to_abandoned(self, decision=None, context=None): return self.write({'state': 'abandoned', 'decision': None}) @api.multi def report_send(self): """ Open a window to compose an email, with the default template message loaded by default """ self.ensure_one() template = self.env.ref( 'school_evaluations.email_template_success_certificate', False) compose_form = self.env.ref('mail.email_compose_message_wizard_form', False) ctx = dict( default_model='school.individual_bloc', default_res_id=self.id, default_use_template=bool(template), default_template_id=template.id, default_composition_mode='comment', mark_invoice_as_sent=True, ) return { 'name': _('Compose Email'), 'type': 'ir.actions.act_window', 'view_type': 'form', 'view_mode': 'form', 'res_model': 'mail.compose.message', 'views': [(compose_form.id, 'form')], 'view_id': compose_form.id, 'target': 'new', 'context': ctx, } @api.depends('course_group_ids.total_credits', 'course_group_ids.total_hours', 'course_group_ids.acquiered', 'course_group_ids.dispense', 'course_group_ids.first_session_computed_result_bool', 'course_group_ids.is_ghost_cg') @api.one def compute_credits(self): self.total_acquiered_credits = sum([ icg.total_credits for icg in self.course_group_ids if icg.acquiered == 'A' and not icg.is_ghost_cg ]) self.total_acquiered_hours = sum([ icg.total_hours for icg in self.course_group_ids if icg.acquiered == 'A' and not icg.is_ghost_cg ]) self.total_not_acquiered_credits = self.total_credits - self.total_acquiered_credits self.total_not_acquiered_hours = self.total_hours - self.total_acquiered_hours # WAS BEFORE May 2018 #self.total_dispensed_credits = sum([icg.total_dispensed_credits for icg in self.course_group_ids]) #self.total_dispensed_hours = sum([icg.total_dispensed_hours for icg in self.course_group_ids]) self.total_dispensed_credits = sum([ icg.total_credits for icg in self.course_group_ids if icg.dispense and not icg.is_ghost_cg ]) self.total_dispensed_hours = sum([ icg.total_hours for icg in self.course_group_ids if icg.dispense and not icg.is_ghost_cg ]) self.total_not_dispensed_credits = self.total_credits - self.total_dispensed_credits self.total_not_dispensed_hours = self.total_hours - self.total_dispensed_hours @api.depends('course_group_ids.final_result', 'course_group_ids.total_weight', 'course_group_ids.acquiered', 'course_group_ids.is_ghost_cg') @api.one def compute_evaluation(self): total = 0 total_first = 0 total_second = 0 total_weight = 0 for icg in self.course_group_ids: if icg.acquiered == 'A' and icg.total_weight > 0 and not icg.is_ghost_cg: # if total_weight == 0 means full dispense total += icg.final_result * icg.total_weight total_first += icg.first_session_result * icg.total_weight total_second += icg.second_session_result * icg.total_weight total_weight += icg.total_weight if total_weight > 0: self.evaluation = total / total_weight self.first_session_result = total_first / total_weight self.total_second = total_first / total_weight else: _logger.debug('total_weight is 0 on Bloc %s' % self.name) self.evaluation = None
class dtdream_contract(models.Model): _name = 'dtdream.contract' _inherit = ['mail.thread'] _description = u'合同评审' his_approve = fields.Many2many('hr.employee', string="历史审批人") name = fields.Char(string="合同名称") @api.constrains('constract_type') @api.onchange('constract_type') def _compute_constract_id(self): if self.constract_type: max_constract_id = '' baseid = 'DTD-' + self.constract_type.name_id + time.strftime( "%Y%m%d", time.localtime()) sql_baseid = baseid + "%" self._cr.execute( "select constract_id from dtdream_contract where constract_id like '" + sql_baseid + "' order by id desc limit 1") for rec in self._cr.fetchall(): max_constract_id = rec[0] if max_constract_id: max_id = max_constract_id[15:] if int(max_id) < 9: self.constract_id_copy = baseid + '0' + str( int(max_id) + 1) self.constract_id = baseid + '0' + str(int(max_id) + 1) else: self.constract_id_copy = baseid + str(int(max_id) + 1) self.constract_id = baseid + str(int(max_id) + 1) else: self.constract_id_copy = baseid + '01' self.constract_id = baseid + '01' constract_id = fields.Char(string="合同编号", store=True) constract_id_copy = fields.Char(string="合同编号_copy") constract_type = fields.Many2one("dtdream.contract.config", string="合同类型") constract_type_char = fields.Char(string="合同类型_copy") @api.onchange("constract_type") def _compute_people(self): self.constract_type_char = self.constract_type.name config = self.env['dtdream.contract.config'].search([ ('name', '=', self.constract_type.name) ]) self.legal_interface = config.legal_interface self.review_ids = config.review_ids self.huiqian_ids = config.huiqian_ids self.quanqian_id = config.quanqian_id self.stamp_id = config.stamp_id self.file_id = config.file_id applicant = fields.Many2one('hr.employee', string='申请人', default=lambda self: self.env['hr.employee']. search([('user_id', '=', self.env.user.id)])) @api.one @api.onchange("applicant") def _compute_employee(self): self.job_number = self.applicant.job_number self.deparment = self.applicant.department_id.complete_name self.tel_number = self.applicant.mobile_phone self.deparment_manage = self.applicant.department_id.manager_id.id # job_number=fields.Char(string="工号",compute=_compute_employee) def _compute_is_applicant(self): # 判断是否是申请人或者创建人 if self.state: if self.env.user.id != self.applicant.user_id.id and self.env.user.id != self.create_uid.id: self.is_applicant = False else: self.is_applicant = True else: self.is_applicant = True # 判断是否是主管 if self.env.user.id != self.deparment_manage.user_id.id: self.is_manager = False else: self.is_manager = True #判断是否是归档人 if self.env.user.id != self.file_id.user_id.id: self.is_file_id = False else: self.is_file_id = True is_applicant = fields.Boolean(string="是否申请人或创建人", compute=_compute_is_applicant, default=True) is_manager = fields.Boolean(string="是否是主管", compute=_compute_is_applicant) job_number = fields.Char(string="工号", compute=_compute_employee) deparment = fields.Char(string="部门", compute=_compute_employee) tel_number = fields.Char(string="电话", compute=_compute_employee) deparment_manage = fields.Many2one('hr.employee', string="部门主管") state = fields.Selection([("0", "草稿"), ("1", "主管审批"), ("2", "待组织评审"), ("3", "评审中"), ("4", "待组织会签"), ("5", "会签中"), ("6", "权签中"), ("7", "待盖章"), ("8", "待归档"), ("9", "闭环"), ("10", "作废")], string="合同状态") create_time = fields.Datetime(string='创建时间', default=lambda self: datetime.now(), readonly=1) money = fields.Float(string="合同金额(元)") @api.onchange('money') def _compute_money_final(self): self.money_final = self.money money_final = fields.Float(string="合同最终金额(元)", help='合同最终金额由合同归档人录入') background = fields.Text(string="合同背景介绍") attachment = fields.Binary(string="合同附件(初稿)", store=True) attachment_name = fields.Char(string="附件名") att_final = fields.Binary(string="合同附件(最新稿)") att_final_name = fields.Char(string="合同终稿附件名") remark = fields.Char(string="备注") pro_name = fields.Many2one("crm.lead", string="项目名称") tip = fields.Char(default=lambda self: self.env['dtdream.contract.url']. search([], limit=1).name) @api.onchange('pro_name') @api.depends('pro_name') def _compute_parter(self): try: self.partner = self.env['crm.lead'].search([ ('name', '=', self.pro_name.name) ]).partner_id.name except: return partner = fields.Char(string="合作方", compute=_compute_parter, store=True) provider = fields.Many2one('res.partner', string='合作方', domain=[('supplier', '=', True)]) is_standard = fields.Boolean(string="是标准合同", help='标准合同无需评审,直接进入会签环节,可快速完成合同评审流程') current_handler_ids = fields.Many2many('hr.employee', 'c_i_h_e', string="当前处理人", store=True) @api.one def _compute_is_handler(self): for handler in self.current_handler_ids: if handler.user_id.id == self.env.user.id: self.is_handler = True @api.one def _compute_is_legal_interface(self): self.is_legal_interface = False for people in self.legal_interface: if people.user_id.id == self.env.user.id: self.is_legal_interface = True is_handler = fields.Boolean("是否当前处理人", compute=_compute_is_handler) is_legal_interface = fields.Boolean("是否法务部接口人", compute=_compute_is_legal_interface) legal_interface = fields.Many2many('hr.employee', 'dt_contract_legal_interface', string="法务接口人") review_ids = fields.Many2many('hr.employee', 'r_i_h_e', string="评审人", store=True) huiqian_ids = fields.Many2many('hr.employee', 'h_i_h_e', string="会签人") quanqian_id = fields.Many2one('hr.employee', string="权签人") stamp_id = fields.Many2one('hr.employee', string="盖章处理人") def _compute_stamp(self): self.is_stamped = True self.stamp_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) def _compute_file(self): self.is_filed = True self.file_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) is_stamped = fields.Boolean(string="是否已盖章") stamp_time = fields.Char(string="盖章时间") file_id = fields.Many2one('hr.employee', string="归档处理人") is_filed = fields.Boolean(string="是否已归档") is_file_id = fields.Boolean(string='是否归档人', compute=_compute_is_applicant) file_time = fields.Char(string="归档时间") review2wait_countersign = fields.Boolean(default=False) def send_email(self, cr, uid, id): base_url = self.pool.get('ir.config_parameter').get_param( cr, uid, 'web.base.url') link = '/web?#id=%s&view_type=form&model=dtdream.contract' % id url = base_url + link damn_self = self.pool.get('dtdream.contract').browse(cr, uid, id) for people in damn_self.current_handler_ids: damn_self.env['mail.mail'].create({ 'subject': u'%s于%s提交合同:%s 评审申请,请您审批!' % (damn_self.applicant.name, damn_self.create_time[:10], damn_self.name), 'body_html': u''' <p>%s,您好:</p> <p>%s提交的合同评审正等待您的审批!</p> <p> 请点击链接进入审批: <a href="%s">%s</a></p> <p>dodo</p> <p>万千业务,简单有do</p> <p>%s</p>''' % (people.name, damn_self.applicant.name, url, url, damn_self.write_date[:10]), 'email_from': damn_self.env['ir.mail_server'].search([], limit=1).smtp_user, 'email_to': people.work_email, }).send() return url @api.one def copy(self, default=None): default = dict(default or {}, constract_id="") print default return super(dtdream_contract, self).copy(default=default) @api.model def create(self, vals): config = self.env['dtdream.contract.config'].search([ ('name', '=', vals['constract_type_char']) ]) result = super(dtdream_contract, self).create(vals) # 对只读字段重新写入 result.legal_interface = config.legal_interface result.huiqian_ids = config.huiqian_ids result.quanqian_id = config.quanqian_id result.stamp_id = config.stamp_id result.file_id = config.file_id result.money_final = result.money result.constract_id = result.constract_id_copy # result.message_post(body=u"--------------:%s"%time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) # 创建人不是申请人添加到关注者 if self._context['uid'] != result.applicant.user_id.id: result.message_subscribe_users( user_ids=[result.applicant.user_id.id]) return result @api.multi def write(self, vals): if self.state == '0': if vals.has_key('money'): self.money_final = vals['money'] if vals.has_key('constract_type_char'): config = self.env['dtdream.contract.config'].search([ ('name', '=', vals['constract_type_char']) ]) self.legal_interface = config.legal_interface self.huiqian_ids = config.huiqian_ids self.quanqian_id = config.quanqian_id self.stamp_id = config.stamp_id self.file_id = config.file_id self.constract_id = vals['constract_id_copy'] result = super(dtdream_contract, self).write(vals) return result _sql_constraints = [('constract_id_unique', 'unique(constract_id)', '合同编号重复,请刷新页面重新填写!')] @api.multi def wkf_draft(self): # 草稿状态 self.current_handler_ids = "" self.state = '0' # self.write({'state':'0'}) @api.multi def wkf_manager_review(self): # 主管审批 self.message_post( body=u"合同编号:%s,提交,提交时间:%s" % (self.constract_id, (datetime.now() + relativedelta(hours=8)).strftime("%Y-%m-%d %H:%M:%S"))) self.current_handler_ids = self.deparment_manage self.state = '1' self.send_email() @api.multi def wkf_wait_review(self): # 待组织评审 self.current_handler_ids = self.legal_interface self.state = '2' self.send_email() @api.multi def wkf_review(self): # 评审中 self.current_handler_ids = self.review_ids self.state = '3' self.send_email() @api.multi def wkf_wait_countersign(self): # 待组织会签 self.current_handler_ids = self.legal_interface self.state = '4' self.send_email() @api.multi def wkf_countersign(self): # 会签中 self.current_handler_ids = self.huiqian_ids self.state = '5' self.send_email() @api.multi def wkf_sign(self): # 权签中 self.current_handler_ids = self.quanqian_id self.state = '6' self.send_email() @api.multi def wkf_stamp(self): # 待盖章 self.current_handler_ids = self.stamp_id self.state = '7' self.send_email() @api.multi def wkf_file(self): # 待归档 self._compute_stamp() self.current_handler_ids = self.file_id self.state = '8' self.send_email() @api.multi def wkf_done(self): self._compute_file() self.current_handler_ids = '' self.state = '9' @api.multi def wkf_void(self): self.current_handler_ids = '' self.state = '10' @api.multi def _message_track(self, tracked_fields, initial): self.ensure_one() changes = set() tracking_value_ids = [] # generate tracked_values data structure: {'col_name': {col_info, new_value, old_value}} for col_name, col_info in tracked_fields.items(): initial_value = initial[col_name] new_value = getattr(self, col_name) if new_value != initial_value and ( new_value or initial_value): # because browse null != False tracking = self.env[ 'mail.tracking.value'].create_tracking_values( initial_value, new_value, col_name, col_info) if tracking: tracking_value_ids.append([0, 0, tracking]) if col_name in tracked_fields: changes.add(col_name) return changes, tracking_value_ids @api.multi def message_track(self, tracked_fields, initial_values): return @api.cr_uid_ids_context def message_post(self, cr, uid, thread_id, context=None, **kwargs): current_contract = self.pool.get('dtdream.contract').browse( cr, uid, thread_id, context=context) if kwargs.has_key('attachment_ids'): for id in kwargs['attachment_ids']: current_contract.att_final = self.pool.get( 'ir.attachment').browse(cr, uid, id, context=context).datas current_contract.att_final_name = self.pool.get( 'ir.attachment').browse(cr, uid, id, context=context).name base1 = current_contract.pool.get( 'ir.config_parameter').get_param( current_contract.env.cr, current_contract.env.user.id, 'web.base.url') link1 = link = '/web?#id=%s&view_type=form&model=dtdream.contract' % thread_id url1 = base1 + link1 for people in current_contract.current_handler_ids: mess = current_contract.env['mail.mail'].create({ 'subject': u'%s于%s提交合同:%s 评审申请已经重新上传了附件,请您审批!' % (current_contract.applicant.name, current_contract.create_time[:10], current_contract.name), 'body_html': u''' <p>%s,您好:</p> <p>%s提交的合同评审已经重新上传了附件正等待您的审批!</p> <p> 请点击链接进入审批: <a href="%s">%s</a></p> <p>dodo</p> <p>万千业务,简单有do</p> <p>%s</p>''' % (people.name, current_contract.applicant.name, url1, url1, current_contract.write_date[:10]), 'email_from': current_contract.env['ir.mail_server'].search( [], limit=1).smtp_user, 'email_to': people.work_email, # 'model': "", }) mess.write({'model': ""}) mess.send() return super(dtdream_contract, self).message_post(cr, uid, thread_id, context=context, **kwargs) @api.model def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False): params = self._context.get('params', None) action = params.get("action", 0) if params else 0 my_action = self.env["ir.actions.act_window"].search([('id', '=', action)]) res = super(dtdream_contract, self).fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=False) doc = etree.XML(res['arch']) if my_action.name != u"我的申请": if res['type'] == "form": doc.xpath("//form")[0].set("create", "false") if res['type'] == "tree": doc.xpath("//tree")[0].set("create", "false") res['arch'] = etree.tostring(doc) return res
class XLSXReportPabiHiringSummaryResults(models.Model): _name = 'xlsx.report.pabi.hiring.summary.results' _auto = False _description = 'Temp table as ORM holder' partner_id = fields.Many2one( 'res.partner', string='Partner', ) category_id = fields.Many2one( 'res.partner.category', string='Partner Category', ) operating_unit_id = fields.Many2one( 'operating.unit', string='OU', ) org_id = fields.Many2one( 'res.org', string='Org', ) po_name = fields.Char( string='PO Name', readonly=True, ) pol_name = fields.Char( string='POL Name', readonly=True, ) costcenter_name = fields.Char( string='Center Name', readonly=True, ) method_name = fields.Char( string='Method Name', readonly=True, ) amount_total = fields.Float( string='Amount Total', readonly=True, ) currency = fields.Char( string='Currency', readonly=True, ) po_date = fields.Date( string='PO Date', readonly=True, ) pd_date = fields.Date( string='PD Date', readonly=True, ) date_contract_start = fields.Date( string='Contract Start Date', readonly=True, ) date_contract_end = fields.Date( string='Contract End Date', readonly=True, ) def init(self, cr): tools.drop_view_if_exists(cr, self._table) cr.execute("""CREATE or REPLACE VIEW %s as ( SELECT row_number() over (order by po.id) as id, po.name as po_name, po.amount_total, cur.name as currency, po.date_order as po_date, ou.id as operating_unit_id, (Case when ou.name = 'สำนักงานกลาง' then 'สำนักงานพัฒนาวิทยาศาสตร์และเทคโนโลยีแห่งชาติ' else ou.name end) as ou_name, rp.id as partner_id, ( SELECT prl.name FROM purchase_request_line prl WHERE id = prpol.purchase_request_line_id LIMIT 1 ) as prl_name, wa.date_contract_start as date_contract_start, wa.date_contract_end as date_contract_end, org.id as org_id, rpc.id as category_id FROM purchase_order_line pol LEFT JOIN purchase_order po on po.id = pol.order_id LEFT JOIN purchase_request_purchase_order_line_rel prpol on prpol.purchase_order_line_id = pol.id LEFT JOIN res_partner rp on rp.id = po.partner_id LEFT JOIN res_partner_category rpc on rpc.id = rp.category_id LEFT JOIN product_template pt on pt.id = pol.product_id LEFT JOIN product_category categ ON categ.id = pt.categ_id LEFT JOIN purchase_work_acceptance wa ON wa.order_id = po.id LEFT JOIN operating_unit ou on ou.id = po.operating_unit_id LEFT JOIN res_org org on org.operating_unit_id = ou.id LEFT JOIN res_currency cur on cur.id = po.currency_id WHERE rp.supplier = True AND rp.employee = False AND wa.state = 'done' AND po.state not in ('cancel' ,'draft') AND categ.name like '%%จ้าง%%' )""" % (self._table, ))
class wiz_mo_repair(models.TransientModel): """ Wiz manufacturing order for repair """ _name = 'wiz.mo.repair' _description = 'Wiz manufacturing order for repair' _rec_name = 'origin_num_serie_id' @api.one @api.depends('origin_num_serie_id') def _compute_origin_num_serie_id(self): """ All origin_num_serie_id """ self.product_id = self.origin_num_serie_id.product_id.id self.uom_id = self.origin_num_serie_id.uom_id.id self.qty_label = self.origin_num_serie_id.uom_qty @api.model def _repair_type_get(self): return [ ('repair_overhead', _('Repair overhead')), ('repair_subset', _('Repair subset')), ] @api.one @api.depends('routing_id') def _compute_all_operation_ids(self): """ All routing_id """ all_operation_ids = [] for ope in self.routing_id.routing_line_ids: all_operation_ids.append((4, ope.id)) self.all_operation_ids = all_operation_ids @api.one @api.depends('disassembly_rl_id', 'assembly_rl_id', 'mo_repair_ope_ids') def _compute_use_operation_ids(self): """ All routing_id """ use_operation_ids = [] if self.disassembly_rl_id: use_operation_ids.append((4, self.disassembly_rl_id.id)) if self.assembly_rl_id: use_operation_ids.append((4, self.assembly_rl_id.id)) for mo_repair_ope in self.mo_repair_ope_ids: use_operation_ids.append((4, mo_repair_ope.rl_id.id)) self.use_operation_ids = use_operation_ids #=========================================================================== # COLUMNS #=========================================================================== repair_date = fields.Date(string='Repair date', default=lambda self: fields.Date.today()) return_date = fields.Date(string='Return date') cause = fields.Text(string='Cause') repair_type = fields.Selection('_repair_type_get', string='Repair type', required=True) origin_num_serie_id = fields.Many2one('stock.label', string='Origin serial num', required=True, ondelete='cascade') is_new_num_serie = fields.Boolean(string='Create a new serial num', default=False) wmrl_ids = fields.One2many('wiz.mo.repair.label', 'wiz_mo_repair_id', string='Labels') disassembly_rl_id = fields.Many2one('mrp.routing.line', string='Disassembly operation', required=False, ondelete='cascade') mo_repair_ope_ids = fields.One2many('wiz.mo.repair.operations', 'wiz_mo_repair_id', string='Routing lines') assembly_rl_id = fields.Many2one('mrp.routing.line', string='Assembly operation', required=False, ondelete='cascade') routing_id = fields.Many2one('mrp.routing', string='Routing', required=True, ondelete='cascade') quantity = fields.Float(string='Quantity', default=1.0, required=True) product_id = fields.Many2one('product.product', string='Product', compute='_compute_origin_num_serie_id', store=True) origin_product_id = fields.Many2one('product.product', string='Origin product product', ondelete='cascade') uom_id = fields.Many2one('product.uom', string='UoM', compute='_compute_origin_num_serie_id', store=True) qty_label = fields.Float(string='Label qty', compute='_compute_origin_num_serie_id', store=True) intervention_id = fields.Many2one('intervention', string='Intervention', required=False, ondelete='cascade') all_operation_ids = fields.Many2many('mrp.routing.line', 'wiz_mo_repair_all_rl_rel', 'wiz_id', 'rl_id', string='All operations', compute='_compute_all_operation_ids') use_operation_ids = fields.Many2many('mrp.routing.line', 'wiz_mo_repair_use_rl_rel', 'wiz_id', 'rl_id', string='Use operations', compute='_compute_use_operation_ids') #=========================================================================== # Button #=========================================================================== @api.multi def action_validation(self): wo_obj = self.env['mrp.workorder'] product_obj = self.env['product.product'] location_obj = self.env['stock.location'] move_obj = self.env['stock.move'] wo_resource_obj = self.env['mrp.wo.resource'] wmro_obj = self.env['wiz.mo.repair.operations'] mlc_obj = self.env['mrp.label.consumption'] product_int_rcs = product_obj.search([('is_int', '=', True)], limit=1) location_out = location_obj.search([('usage', '=', 'production')], limit=1) location_in = location_obj.search([('usage', '=', 'internal')], limit=1) list_sequence = [] for wiz in self: mo_rcs = False intervention_id = wiz.intervention_id and wiz.intervention_id.id or False min_start_date = wiz.intervention_id and wiz.intervention_id.start_date_requested and '%s 06:00:00' % ( wiz.intervention_id.start_date_requested) or False max_end_date = wiz.intervention_id and wiz.intervention_id.ended_date_requested and '%s 16:00:00' % ( wiz.intervention_id.ended_date_requested) or False other_data_arg_mo = { 'intervention_id': intervention_id, 'requested_date': wiz.repair_date, 'max_end_date': wiz.return_date, 'note_manufacturing': wiz.cause, 'min_start_date': min_start_date, 'max_end_date': max_end_date, } other_data_arg_wo = {'intervention_id': intervention_id} if wiz.mo_repair_ope_ids: arg_rl_sequences = [] routing_line_ids = [] for x in self.mo_repair_ope_ids: arg_rl_sequences.append((x.sequence, x.rl_id.id)) routing_line_ids.append(x.rl_id.id) if Decimal(str(x.sequence)) % Decimal(str(10)): raise except_orm( _('Error'), _('The sequence field must be a multiple of 10.')) list_sequence.append(x.sequence) no_wo = { 'quantity': wiz.quantity, 'uom': wiz.uom_id, 'product': wiz.product_id, 'routing_id': wiz.routing_id.id } mo_rcs = wo_obj.add_operation_wo( routing_line_ids, True, False, False, False, arg_rl_sequences=arg_rl_sequences, no_wo=no_wo, other_data_arg=other_data_arg_mo) if wiz.repair_type == 'repair_subset': if not mo_rcs: mo_rcs = wo_obj.create_mo_light( wiz.product_id, wiz.quantity, wiz.uom_id, wiz.routing_id.id, bom_id=False, other_data=other_data_arg_mo) first_wo_rcs = wo_obj.create_wo_without_bom( wiz.disassembly_rl_id, mo_rcs, 1, other_data_arg=other_data_arg_wo) if wiz.mo_repair_ope_ids: last_sequence = wmro_obj.search( [('wiz_mo_repair_id', '=', wiz.id)], order='sequence desc', limit=1).sequence + 10 else: last_sequence = 10 last_wo_rcs = wo_obj.create_wo_without_bom( wiz.assembly_rl_id, mo_rcs, last_sequence, other_data_arg=other_data_arg_wo) elif mo_rcs: first_wo_rcs = wo_obj.search([('mo_id', '=', mo_rcs.id)], order='sequence asc', limit=1) last_wo_rcs = wo_obj.search([('mo_id', '=', mo_rcs.id)], order='sequence desc', limit=1) else: raise except_orm(_('Error'), _('Please choose at least one operation.')) if list_sequence: if wiz.repair_type != 'repair_subset': min_sequence = min(list_sequence) max_sequence = max(list_sequence) if list_sequence.count(min_sequence) > 1: raise except_orm( _('Error'), _('It can not be two times the same minimal sequence.' )) if list_sequence.count(max_sequence) > 1: raise except_orm( _('Error'), _('It can not be two times the same maximum sequence.' )) # Création matières à consommer et produits finaux # Pour le premier OT wo_resource_rcs = wo_resource_obj.search( [('wo_id', '=', first_wo_rcs.id)], order='sequence asc', limit=1) if wo_resource_rcs: location_in_int = wo_resource_rcs.resource_id.location_id else: location_in_int = location_in move_product_rm = self.add_rm(first_wo_rcs, first_wo_rcs.quantity, wiz.uom_id, wiz.product_id, location_in_int.id, location_out.id, move_obj) move_product_rm.assign_label(wiz.origin_num_serie_id, with_scrap=False) mlc_obj.create({ 'wo_id': first_wo_rcs.id, 'label_id': wiz.origin_num_serie_id.id, 'use_consumption': True, 'quantity': wiz.quantity, }) # Pour le dernier OT wo_resource_rcs = wo_resource_obj.search( [('wo_id', '=', last_wo_rcs.id)], order='sequence asc', limit=1) if wo_resource_rcs: location_in_int = wo_resource_rcs.resource_id.location_id else: location_in_int = location_in self.add_fp(last_wo_rcs, last_wo_rcs.quantity, wiz.uom_id, wiz.product_id, location_in_int.id, location_out.id, move_obj) if not wiz.is_new_num_serie: last_wo_rcs.write( {'label_mo_repair_id': wiz.origin_num_serie_id.id}) # Product int wo_int_rcs = wo_obj.search([('mo_id', '=', mo_rcs.id), ('id', 'not in', (first_wo_rcs.id, last_wo_rcs.id))]) for wo_int in wo_int_rcs: self.add_fp(wo_int, wo_int.quantity, product_int_rcs.uom_id, product_int_rcs, location_in_int.id, location_out.id, move_obj) if wiz.repair_type == 'repair_subset': self.add_fp(first_wo_rcs, first_wo_rcs.quantity, product_int_rcs.uom_id, product_int_rcs, location_in_int.id, location_out.id, move_obj) # Liste des sous ensembles à démonter dico_label_ids = {} for wmrl in wiz.wmrl_ids: label = wmrl.label_id if label.product_id in dico_label_ids: dico_label_ids[label.product_id]['uom_qty'] += Decimal( str(wmrl.uom_qty)) dico_label_ids[label.product_id]['label_rcs'] += label dico_label_ids[ label.product_id]['dict_label_qty'][label] = ( wmrl.uom_qty, 0) else: dico_label_ids[label.product_id] = { 'label_rcs': label, 'uom_rcs': label.uom_id, 'uom_qty': Decimal(str(wmrl.uom_qty)), 'dict_label_qty': { label: (wmrl.uom_qty, 0) } } mlc_obj.create({ 'wo_id': last_wo_rcs.id, 'label_id': label.id, 'use_consumption': True, 'quantity': wmrl.uom_qty }) for dico_label in dico_label_ids: move_fp = self.add_fp( first_wo_rcs, dico_label_ids[dico_label]['uom_qty'], dico_label_ids[dico_label]['uom_rcs'], dico_label, location_in_int.id, location_out.id, move_obj) move_fp.assign_label( dico_label_ids[dico_label]['label_rcs'], dict_label_qty=dico_label_ids[dico_label] ['dict_label_qty'], with_scrap=False) self.add_rm(last_wo_rcs, dico_label_ids[dico_label]['uom_qty'], dico_label_ids[dico_label]['uom_rcs'], dico_label, location_in_int.id, location_out.id, move_obj) else: if first_wo_rcs != last_wo_rcs: self.add_fp(first_wo_rcs, first_wo_rcs.quantity, product_int_rcs.uom_id, product_int_rcs, location_in_int.id, location_out.id, move_obj) # Construction des suivants/précédents if first_wo_rcs != last_wo_rcs: new_sequence_next_rcs = wo_obj.search( [('mo_id', '=', mo_rcs.id), ('sequence', '>', first_wo_rcs.sequence)], order='sequence asc', limit=1) if new_sequence_next_rcs: new_sequence_next = new_sequence_next_rcs.read( ['sequence'])[0]['sequence'] self.recursive_prev_next_wo(mo_rcs, first_wo_rcs, new_sequence_next, wo_obj) return { 'name': _('Manufacturing order for repair'), 'view_type': 'form', 'view_mode': 'form', 'res_model': 'mrp.manufacturingorder', 'type': 'ir.actions.act_window', 'target': 'current', 'res_id': mo_rcs.id, 'nodestroy': True } return {'type': 'ir.actions.act_window_close'} def add_rm(self, wo, quantity, uom, product, location_id, location_dest_id, move_obj): efficient_unit_qty = wo.quantity and float( quantity) / wo.quantity or float(quantity) move = move_obj.create_move(product, location_id, location_dest_id, qty=float(quantity), uom=uom, other_data={ 'wo_incoming_id': wo.id, 'efficient_unit_qty': efficient_unit_qty, 'is_forecast': wo.is_forecast, }, in_product_uom=True) return move def add_fp(self, wo, quantity, uom, product, location_id, location_dest_id, move_obj): efficient_unit_qty = wo.quantity and float( quantity) / wo.quantity or float(quantity) move = move_obj.create_move(product, location_dest_id, location_id, qty=float(quantity), uom=uom, other_data={ 'wo_outgoing_id': wo.id, 'efficient_unit_qty': efficient_unit_qty, 'is_forecast': wo.is_forecast, }, in_product_uom=True) return move def recursive_prev_next_wo(self, mo_rcs, wo_prec_rcs, sequence_next, wo_obj): wo_next_rcs = wo_obj.search([('mo_id', '=', mo_rcs.id), ('sequence', '=', sequence_next)]) wo_prec_rcs.write({'next_wo_ids': [(6, 0, wo_next_rcs.ids)]}) new_sequence_next_rcs = wo_obj.search( [('mo_id', '=', mo_rcs.id), ('sequence', '>', wo_next_rcs[0].sequence)], order='sequence asc', limit=1) if new_sequence_next_rcs: new_sequence_next = new_sequence_next_rcs.read(['sequence' ])[0]['sequence'] self.recursive_prev_next_wo(mo_rcs, wo_next_rcs, new_sequence_next, wo_obj) return True @api.multi def action_select_label(self): for wiz in self: wmrsl = self.env['wiz.mo.repair.select.label'].create( {'wiz_mo_repair_id': wiz.id}) return { 'name': _('Select labels'), 'view_type': 'form', 'view_mode': 'form', 'res_model': 'wiz.mo.repair.select.label', 'type': 'ir.actions.act_window', 'target': 'stack', 'res_id': wmrsl.id, 'nodestroy': True, }
class SectionBudgetTransfer(models.Model): _name = 'section.budget.transfer' _inherit = ['mail.thread'] # _inherit = ['mail.thread', 'ir.needaction_mixin'] _description = "Section Budget Transfer" _order = 'id desc' # # _track = { # 'state': { # 'pabi_budget_transfer.mt_transferd_draft_to_confirmed': # lambda self, cr, uid, obj, ctx=None: obj.state == 'confirm', # }, # } # @api.model # def _needaction_domain_get(self): # """ Show as unread to everyone as it is transfered """ # return [('state', '=', 'draft')] name = fields.Char( string='Name', required=True, readonly=True, default='/', size=100, ) fiscalyear_id = fields.Many2one( 'account.fiscalyear', string='Fiscal Year', required=True, readonly=True, # states={'draft': [('readonly', False)]}, default=lambda self: self.env['account.fiscalyear'].find(), help="Fiscalyear will be as of current date only, no backdate allowed" ) division_id = fields.Many2one( 'res.division', string='Division', required=True, readonly=True, default=lambda self: self.env.user.partner_id.employee_id.section_id.division_id, ) org_ids = fields.Many2many( 'res.org', string='Org', required=True, readonly=True, default=lambda self: self._default_org_ids(), ) currency_id = fields.Many2one( 'res.currency', string="Currency", default=lambda self: self.env.user.company_id.currency_id, readonly=True, ) date_prepare = fields.Date( string="Prepare Date", default=lambda self: fields.Date.context_today(self), readonly=True, states={'draft': [('readonly', False)]}, ) date_approve = fields.Date( string="Approved Date", readonly=True, ) date_transfer = fields.Date( string="Transfer Date", readonly=True, ) preparer_user_id = fields.Many2one( 'res.users', string='Preparer', default=lambda self: self.env.user, readonly=True, states={'draft': [('readonly', False)]}, ) approver_user_id = fields.Many2one( 'res.users', string='Approver', readonly=True, ) transfer_user_id = fields.Many2one( 'res.users', string='Transferer', readonly=True, ) state = fields.Selection( _TRANSFER_STATE, string='Status', default='draft', index=True, readonly=True, copy=False, track_visibility='onchange', ) transfer_line_ids = fields.One2many( 'section.budget.transfer.line', 'transfer_id', string='Budget Transfer Lines', readonly=True, states={'draft': [('readonly', False)]}, ) notes = fields.Text( string='Additional Information', size=1000, ) total_transfer_amt = fields.Float( string='Transferred Amount', compute='_compute_total_transfer_amt', ) @api.model def _default_org_ids(self): org_origin = self.env.user.partner_id.employee_id.section_id.org_id org_addition = self.env.user.partner_id.employee_id.org_ids return org_origin + org_addition @api.multi @api.constrains('fiscalyear_id', 'transfer_line_ids') def _check_transfer_line(self): """ Check that, all budget selected must be * chart_view = 'unit_base' * Same fiscal as the header * Belong to the same Org * Must be in state draft """ for trans in self: for l in trans.transfer_line_ids: # State if l.from_budget_id.state != 'draft' or \ l.to_budget_id.state != 'draft': raise ValidationError(_('Please verify that all budgets ' 'are in draft state!')) # Unit based if l.from_budget_id.chart_view != 'unit_base' or \ l.to_budget_id.chart_view != 'unit_base': raise ValidationError( _('Please verify that all budgets are unit based')) # Fiscal year this_fy_id = self.env['account.fiscalyear'].find() this_fy = self.env['account.fiscalyear'].browse(this_fy_id) if this_fy.id != trans.fiscalyear_id.id: raise ValidationError( _('Current FY is %s, you are not allow to transfer ' 'budget out of this fiscalyear.') % (this_fy.name)) if l.from_budget_id.fiscalyear_id != trans.fiscalyear_id or \ l.to_budget_id.fiscalyear_id != trans.fiscalyear_id: raise ValidationError( _('Please verify that all budgets are on fiscal ' 'year %s') % (trans.fiscalyear_id.name)) # Org # kittiu: this result in error during save # if l.from_budget_id.org_id != trans.org_id or \ # l.to_budget_id.org_id != trans.org_id: # raise ValidationError( # _('Please verify that all budgets belong to Org %s') % # (trans.org_id.name_short)) # Not same budget if l.from_budget_id == l.to_budget_id: raise ValidationError(_('Please verify that source and ' 'target budget are not same!')) @api.depends('transfer_line_ids', 'transfer_line_ids.amount_transfer', 'state') def _compute_total_transfer_amt(self): for record in self: if record.state == 'transfer': lines = record.transfer_line_ids total_amt = sum([i.amount_transfer for i in lines]) record.total_transfer_amt = total_amt @api.multi def button_draft(self): self.write({'state': 'draft'}) return True @api.multi def button_cancel(self): self.write({'state': 'cancel'}) return True @api.multi def button_confirm(self): fiscalyear_id = self.env['account.fiscalyear'].find() for record in self: # 1) Lines if sum(record.transfer_line_ids.mapped('amount_transfer')) == 0.0: raise ValidationError( _('You can not confirm without transfer line amount!')) # 2) Can't transfer > room for line in record.transfer_line_ids: balance = line._get_balance() if float_compare(line.amount_transfer, balance, 2) == 1: raise ValidationError( _('Your amount is bigger than ' 'available amount to transfer!')) name = self.env['ir.sequence'].\ with_context(fiscalyear_id=fiscalyear_id).\ next_by_code('section.budget.transfer') record.write({'state': 'confirm', 'name': name}) return True @api.multi def button_approve(self): self.write({'state': 'approve', 'date_approve': fields.Date.context_today(self), 'approver_user_id': self._uid}) return True @api.multi def button_transfer(self): for transfer in self: transfer.transfer_line_ids.action_transfer() self.write({'state': 'transfer', 'date_transfer': fields.Date.today(), 'transfer_user_id': self._uid}) return True @api.multi def unlink(self): for rec in self: if rec.state != 'draft': raise ValidationError( _('You can not delete non-draft records!')) return super(SectionBudgetTransfer, self).unlink()
class IndividualCourse(models.Model): '''Individual Course''' _inherit = 'school.individual_course' ## Type and weight for average computation (ie 0 for dispenses) ## type = fields.Selection(([('S', 'Simple'), ('C', 'Complex'), ('D', 'Deferred')]), compute='compute_type', string='Type', store=True, default="S") c_weight = fields.Float(compute='compute_weight', readonly=True, store=True) ## Evaluation ## ann_result = fields.Char(string='Annual Result', track_visibility='onchange') jan_result = fields.Char(string='January Result', track_visibility='onchange') jun_result = fields.Char(string='June Result', track_visibility='onchange') sept_result = fields.Char(string='September Result', track_visibility='onchange') ## First Session ## first_session_result = fields.Float(compute='compute_results', string='First Session Result', store=True, group_operator='avg', digits=dp.get_precision('Evaluation')) first_session_result_bool = fields.Boolean(compute='compute_results', string='First Session Active', store=True) first_session_note = fields.Text(string='First Session Notes') first_session_result_disp = fields.Char( string='Final Result Display', compute='compute_first_session_result_disp') @api.one def compute_first_session_result_disp(self): if not self.first_session_result_bool: self.first_session_result_disp = "" if self.dispense: self.first_session_result_disp = "Val" else: self.first_session_result_disp = "%.2f" % self.first_session_result ## Second Session ## second_session_result = fields.Float(compute='compute_results', string='Second Session Result', store=True, group_operator='avg', digits=dp.get_precision('Evaluation')) second_session_result_bool = fields.Boolean(compute='compute_results', string='Second Session Active', store=True) second_session_note = fields.Text(string='Second Session Notes') second_session_result_disp = fields.Char( string='Final Result Display', compute='compute_second_session_result_disp') @api.one def compute_second_session_result_disp(self): if not self.second_session_result_bool: self.second_session_result_disp = "" if self.dispense: self.second_session_result_disp = "D" else: self.second_session_result_disp = "%.2f" % self.second_session_result @api.model def create(self, values): if not (values.get('type', False)) and values.get( 'source_course_id', False): course = self.env['school.course'].browse( values['source_course_id']) values['type'] = course.type or 'S' result = super(IndividualCourse, self).create(values) return result @api.one @api.depends('dispense', 'weight', 'jun_result') def compute_weight(self): _logger.debug('Trigger "compute_weight" on Course %s' % self.name) if self.dispense and not self.jun_result: self.c_weight = 0 else: self.c_weight = self.weight @api.one @api.depends('dispense', 'source_course_id.type') def compute_type(self): _logger.debug('Trigger "compute_type" on Course %s' % self.name) if self.dispense: self.type = 'D' else: self.type = self.source_course_id.type def _parse_result(self, input): f = float(input) if (f < 0 or f > 20): raise ValidationError("Evaluation shall be between 0 and 20") else: return f @api.depends('type', 'ann_result', 'jan_result', 'jun_result', 'sept_result') @api.one def compute_results(self): _logger.debug('Trigger "compute_results" on Course %s' % self.name) if self.type == 'D': if self.jun_result: try: f = self._parse_result(self.jun_result) self.first_session_result = f self.first_session_result_bool = True except ValueError: self.first_session_result = 0 self.first_session_result_bool = False raise UserError( _('Cannot decode %s in June Result, please encode a Float eg "12.00".' % self.jun_result)) if self.type in ['S', 'D']: f = -1 if self.jan_result: try: f = self._parse_result(self.jan_result) except ValueError: self.first_session_result = 0 self.first_session_result_bool = False raise UserError( _('Cannot decode %s in January Result, please encode a Float eg "12.00".' % self.jan_result)) if self.jun_result: try: f = self._parse_result(self.jun_result) except ValueError: self.first_session_result = 0 self.first_session_result_bool = False raise UserError( _('Cannot decode %s in June Result, please encode a Float eg "12.00".' % self.jun_result)) if f >= 0: self.first_session_result = f self.first_session_result_bool = True if self.sept_result: try: f = self._parse_result(self.sept_result) self.second_session_result = f self.second_session_result_bool = True except ValueError: self.second_session_result = 0 self.second_session_result_bool = False raise UserError( _('Cannot decode %s in September Result, please encode a Float eg "12.00".' % self.sept_result)) if self.type in ['C']: ann = None jan = None if self.ann_result: try: ann = self._parse_result(self.ann_result) except ValueError: raise UserError( _('Cannot decode %s in January Result, please encode a Float eg "12.00".' % self.ann_result)) if self.jan_result: try: jan = self._parse_result(self.jan_result) except ValueError: raise UserError( _('Cannot decode %s in January Result, please encode a Float eg "12.00".' % self.jan_result)) if self.jun_result: try: jun = self._parse_result(self.jun_result) if self.ann_result and self.jan_result: self.first_session_result = ann * 0.5 + ( jan * 0.5 + jun * 0.5) * 0.5 self.first_session_result_bool = True elif self.ann_result: self.first_session_result = ann * 0.5 + jun * 0.5 self.first_session_result_bool = True else: self.first_session_result = 0 self.first_session_result_bool = False except ValueError: self.first_session_result = 0 self.first_session_result_bool = False raise UserError( _('Cannot decode %s in June Result, please encode a Float eg "12.00".' % self.jun_result)) if self.sept_result: try: sept = self._parse_result(self.sept_result) if self.ann_result: self.second_session_result = ann * 0.5 + sept * 0.5 self.second_session_result_bool = True else: self.first_session_result = 0 self.first_session_result_bool = False except ValueError: self.second_session_result = 0 self.second_session_result_bool = False raise UserError( _('Cannot decode %s in September Result, please encode a Float eg "12.00".' % self.sept_result))
class SectionBudgetTransferLine(models.Model): _name = 'section.budget.transfer.line' _description = "Section Budget Transfer Lines" transfer_id = fields.Many2one( 'section.budget.transfer', string='Section Budget Transfer', ondelete='cascade', index=True, ) state = fields.Selection( _TRANSFER_STATE, string='Status', related='transfer_id.state', readonly=True, store=True, ) from_budget_id = fields.Many2one( 'account.budget', string='From Section', required=True, domain=lambda self: self._domain_budget_id() ) from_budget = fields.Char( string='From Budget', related='from_budget_id.name', readonly=True, ) from_section_id = fields.Many2one( 'res.section', string='From Secton', related='from_budget_id.section_id', readonly=True, ) amount_transfer = fields.Float( string='Transfer Amount', required=True, ) to_budget_id = fields.Many2one( 'account.budget', string='To Section', required=True, domain=lambda self: self._domain_budget_id() ) to_budget = fields.Char( string='To Budget', related='to_budget_id.name', readonly=True, ) to_section_id = fields.Many2one( 'res.section', string='To Secton', related='to_budget_id.section_id', readonly=True, ) notes = fields.Text( string='Notes/Reason', size=1000, ) _sql_constraints = [ ('no_negative_transfer_amount', 'CHECK(amount_transfer >= 0)', 'Transfer amount must be positive'), ] def _domain_budget_id(self): org_origin = self.env.user.partner_id.employee_id.section_id.org_id org_addition = self.env.user.partner_id.employee_id.org_ids org_ids = org_origin + org_addition fiscalyear_id = self.env['account.fiscalyear'].find() dom = [('state', '=', 'draft'), ('chart_view', '=', 'unit_base'), ('section_id', '!=', False), ('org_id', 'in', org_ids.ids), ('fiscalyear_id', '=', fiscalyear_id)] return dom @api.multi def action_transfer(self): # Only available setup to use section budget transfer it, for line in self: from_budget = line.from_budget_id to_budget = line.to_budget_id # Check budget level from_budget_release = from_budget.budget_level_id.budget_release to_budget_release = to_budget.budget_level_id.budget_release if from_budget_release != 'manual_header' or \ to_budget_release != 'manual_header': raise ValidationError( _('Budget level for unit base is not valid for transfer.\n' 'Please make sure Release Type = "Budget Header".')) from_budget.write({ 'to_release_amount': (from_budget.released_amount - line.amount_transfer)}) from_budget._validate_plan_vs_release() to_budget.write({ 'to_release_amount': (to_budget.released_amount + line.amount_transfer)}) to_budget._validate_plan_vs_release() return True @api.onchange('from_budget_id', 'to_budget_id') def _onchange_from_to_budget_id(self): self.ensure_one() if self.from_budget_id and self.to_budget_id: if self.from_budget_id.org_id != self.to_budget_id.org_id: raise ValidationError(_( "Can not transfer budget different org.")) @api.onchange('from_budget_id') def _onchange_from_budget_id(self): balance = self._get_balance() if self.from_budget_id and balance <= 0.0: raise ValidationError( _("%s don't have enough budget to transfer.\n" "Make sure its rolling amount is less than its released") % self.from_budget_id.name) self.amount_transfer = balance @api.onchange('amount_transfer') def _onchange_amount_transfer(self): balance = self._get_balance() if float_compare(self.amount_transfer, balance, 2) == 1: raise ValidationError( _('Your amount is bigger than available amount to transfer!')) @api.multi def _get_balance(self): budget = self.from_budget_id fiscalyear = budget.fiscalyear_id control_external = fiscalyear.control_ext_charge_only if control_external: expense_lines = budget.section_id.monitor_expense_ids.filtered( lambda l: l.fiscalyear_id in fiscalyear and l.charge_type == 'external' ) else: expense_lines = budget.section_id.monitor_expense_ids.filtered( lambda l: l.fiscalyear_id in fiscalyear ) consumed = sum([i.amount_consumed for i in expense_lines]) release = self.from_budget_id.to_release_amount balance = release - consumed return balance
class ComputedPurchaseOrderLine(models.Model): _description = 'Computed Purchase Order Line' _name = 'computed.purchase.order.line' _order = 'sequence' _STATE = [ ('new', 'New'), ('up_to_date', 'Up to date'), ('updated', 'Updated'), ] # Columns section computed_purchase_order_id = fields.Many2one( 'computed.purchase.order', 'Order Reference', required=True, ondelete='cascade') state = fields.Selection( _STATE, 'State', required=True, readonly=True, default='new', help="Shows if the product's information has been updated") sequence = fields.Integer( 'Sequence', help="""Gives the sequence order when displaying a list of""" """ purchase order lines.""") product_id = fields.Many2one( 'product.product', 'Product', required=True, domain=[('purchase_ok', '=', True)]) uom_id = fields.Many2one( related='product_id.uom_id', string="UoM", readonly='True') product_code = fields.Char('Supplier Product Code',) product_code_inv = fields.Char( compute='_get_product_information', inverse='_set_product_code', string='Supplier Product Code', multi='product_code_name_price', help="""This supplier's product code will be used when printing""" """ a request for quotation. Keep empty to use the internal""" """ one.""") product_name = fields.Char('Supplier Product Name',) product_name_inv = fields.Char( compute='_get_product_information', inverse='_set_product_name', string='Supplier Product Name', multi='product_code_name_price', help="""This supplier's product name will be used when printing""" """ a request for quotation. Keep empty to use the internal""" """ one.""") product_price = fields.Float( 'Supplier Product Price', digits_compute=dp.get_precision('Product Price')) discount = fields.Float( string='Discount (%)', digits_compute=dp.get_precision('Discount')) discount_inv = fields.Float( string='Discount (%)', digits_compute=dp.get_precision('Discount'), compute='_get_product_information', inverse='_set_discount', multi='product_code_name_price',) product_price_inv = fields.Float( compute='_get_product_information', inverse='_set_product_price', string='Supplier Product Price', multi='product_code_name_price',) price_policy = fields.Selection( [('uom', 'per UOM'), ('package', 'per Package')], "Price Policy", default='uom', required=True) product_price_inv_eq = fields.Float( compute='_compute_product_price_inv_eq', string='Supplier Product Price per Uom',) subtotal = fields.Float( 'Subtotal', compute='_compute_subtotal_price', digits_compute=dp.get_precision('Product Price')) package_qty = fields.Float('Package quantity') package_qty_inv = fields.Float( compute='_get_product_information', inverse='_set_package_qty', string='Package quantity', multi='product_code_name_price',) weight = fields.Float( related='product_id.weight', string='Net Weight', readonly='True') uom_po_id = fields.Many2one('product.uom', 'UoM', required=True) average_consumption = fields.Float( compute="_compute_average_consumption", digits=(12, 3)) displayed_average_consumption = fields.Float( 'Average Consumption', digits=(12, 3)) consumption_range = fields.Integer( 'Range (days)', help="""Range (in days) used to display the average consumption""") stock_duration = fields.Float( compute='_compute_stock_duration', string='Stock Duration (Days)', readonly='True', help="Number of days the stock should last.") virtual_duration = fields.Float( compute='_compute_stock_duration', string='Virtual Duration (Days)', readonly='True', help="""Number of days the stock should last after""" """ the purchase.""") purchase_qty_package = fields.Float( 'Number of packages', help="""The number of packages you'll buy.""") purchase_qty = fields.Float( 'Quantity to purchase', compute='_compute_purchase_qty', store=True, help="The quantity you should purchase.") manual_input_output_qty = fields.Float( string='Manual variation', default=0, help="""Write here some extra quantity depending of some""" """ input or output of products not entered in the software\n""" """- negative quantity : extra output ; \n""" """- positive quantity : extra input.""") qty_available = fields.Float( compute='_get_qty', string='Quantity On Hand', multi='get_qty', help="The available quantity on hand for this product") incoming_qty = fields.Float( compute='_get_qty', string='Incoming Quantity', help="Virtual incoming entries", multi='get_qty',) outgoing_qty = fields.Float( compute='_get_qty', string='Outgoing Quantity', help="Virtual outgoing entries", multi='get_qty',) virtual_qty = fields.Float( compute='_get_qty', string='Virtual Quantity', help="Quantity on hand + Virtual incoming and outgoing entries", multi='get_qty',) computed_qty = fields.Float( compute='_get_computed_qty', string='Stock', help="The sum of all quantities selected.", digits_compute=dp.get_precision('Product UoM'),) cpo_state = fields.Selection([ ('draft', 'Draft'), ('done', 'Done'), ('canceled', 'Canceled'), ], related='computed_purchase_order_id.state', string='State') # Constraints section _sql_constraints = [( 'product_id_uniq', 'unique(computed_purchase_order_id,product_id)', 'Product must be unique by computed purchase order!'), ] # Columns section @api.multi @api.onchange('purchase_qty') def onchange_purchase_qty(self): for cpol in self: if cpol.package_qty_inv: cpol.purchase_qty_package = cpol.purchase_qty /\ cpol.package_qty_inv @api.multi @api.depends('purchase_qty_package', 'package_qty_inv') def _compute_purchase_qty(self): for cpol in self: if cpol.purchase_qty_package == int(cpol.purchase_qty_package): cpol.purchase_qty = cpol.package_qty_inv *\ cpol.purchase_qty_package @api.multi @api.onchange('package_qty_inv', 'product_price_inv', 'price_policy') def _compute_product_price_inv_eq(self): for line in self: if line.price_policy == 'package': if line.package_qty_inv: line.product_price_inv_eq = line.product_price_inv /\ line.package_qty_inv else: line.product_price_inv_eq = 0 else: line.product_price_inv_eq = line.product_price_inv @api.depends( 'purchase_qty', 'product_price', 'product_price_inv', 'price_policy', 'package_qty_inv', 'discount_inv') @api.multi def _compute_subtotal_price(self): for line in self: net_unit_price =\ line.product_price_inv * (1 - line.discount_inv / 100.0) if line.price_policy == 'package': line.subtotal = line.package_qty_inv and line.purchase_qty *\ net_unit_price / line.package_qty_inv or 0 else: line.subtotal = line.purchase_qty * net_unit_price @api.onchange('displayed_average_consumption', 'consumption_range') @api.multi def _compute_average_consumption(self): for line in self: line.average_consumption = line.consumption_range and\ line.displayed_average_consumption / line.consumption_range\ or 0 # Fields Function section @api.depends('product_id') @api.multi def _get_qty(self): for cpol in self: cpol.qty_available = cpol.product_id.qty_available cpol.incoming_qty = cpol.product_id.incoming_qty cpol.outgoing_qty = cpol.product_id.outgoing_qty cpol.virtual_qty = cpol.qty_available + cpol.incoming_qty - \ cpol.outgoing_qty @api.multi def _get_computed_qty(self): use_pending_qties = False for cpol in self: if cpol.computed_purchase_order_id.compute_pending_quantity: use_pending_qties = True if use_pending_qties: break for cpol in self: q = cpol.qty_available if use_pending_qties: q += cpol.incoming_qty - cpol.outgoing_qty cpol.computed_qty = q @api.multi def _get_product_information(self): psi_obj = self.env['product.supplierinfo'] for cpol in self: if not cpol.product_id: cpol.product_code_inv = None cpol.product_name_inv = None cpol.product_price_inv = 0.0 cpol.discount = 0.0 cpol.price_policy = 'uom' cpol.package_qty_inv = 0.0 elif cpol.state in ('updated', 'new'): cpol.product_code_inv = cpol.product_code cpol.product_name_inv = cpol.product_name cpol.product_price_inv = cpol.product_price cpol.discount_inv = cpol.discount cpol.package_qty_inv = cpol.package_qty else: psi = psi_obj.search([ ('name', '=', cpol.computed_purchase_order_id.partner_id.id), ('product_tmpl_id', '=', cpol.product_id.product_tmpl_id.id)]) if len(psi): psi = psi[0] if psi: cpol.product_code_inv = psi.product_code cpol.product_name_inv = psi.product_name cpol.product_price_inv = psi.base_price cpol.discount_inv = psi.discount cpol.package_qty_inv = psi.package_qty cpol.price_policy = psi.price_policy @api.depends('product_code_inv') def _set_product_code(self): self.product_code = self.product_code_inv if self.state == 'up_to_date': self.state = 'updated' @api.depends('product_name_inv') def _set_product_name(self): self.product_name = self.product_name_inv if self.state == 'up_to_date': self.state = 'updated' @api.depends('product_price_inv') def _set_product_price(self): self.product_price = self.product_price_inv if self.state == 'up_to_date': self.state = 'updated' @api.depends('discount_inv') def _set_discount(self): self.discount = self.discount_inv if self.state == 'up_to_date': self.state = 'updated' @api.onchange('package_qty_inv') def _set_package_qty(self): self.package_qty = self.package_qty_inv if self.state == 'up_to_date': self.state = 'updated' @api.multi @api.depends('purchase_qty') def _compute_stock_duration(self): for cpol in self: if cpol.product_id: if cpol.average_consumption != 0: cpol.stock_duration = ( cpol.computed_qty + cpol.manual_input_output_qty)\ / cpol.average_consumption cpol.virtual_duration = ( cpol.computed_qty + cpol.manual_input_output_qty + cpol.purchase_qty) / cpol.average_consumption # View Section @api.onchange( 'product_code_inv', 'product_name_inv', 'product_price_inv', 'package_qty_inv', 'price_policy', 'discount_inv') def onchange_product_info(self): self.state = 'updated' @api.onchange( 'computed_purchase_order_id', 'product_id', ) def onchange_product_id(self): vals = { 'state': 'new', 'purchase_qty': 0, 'manual_input_output_qty': 0, } if self.product_id: psi_obj = self.env['product.supplierinfo'] pp = self.product_id computed_qty = pp.qty_available if self.computed_purchase_order_id: cpo = self.computed_purchase_order_id # Check if the product is already in the list. products = [x.product_id.id for x in cpo.line_ids] if self.product_id.id in products: raise ValidationError( _('This product is already in the list!')) if cpo.compute_pending_quantity: computed_qty += pp.incoming_qty - pp.outgoing_qty vals.update({ 'qty_available': pp.qty_available, 'incoming_qty': pp.incoming_qty, 'outgoing_qty': pp.outgoing_qty, 'computed_qty': computed_qty, 'weight': pp.weight, 'uom_po_id': pp.uom_id.id, 'product_price_inv': 0, 'discount_inv': 0, 'price_policy': 'uom', 'package_qty_inv': 0, 'average_consumption': pp.displayed_average_consumption, 'consumption_range': pp.display_range, }) # If product is in the supplierinfo, # retrieve values and set state up_to_date psi_id = psi_obj.search([ ('name', '=', self.computed_purchase_order_id.partner_id.id), ('product_tmpl_id', '=', pp.product_tmpl_id.id)]) if psi_id: psi = psi_id[0] vals.update({ 'product_code_inv': psi.product_code, 'product_name_inv': psi.product_name, 'product_price_inv': psi.price, 'discount_inv': psi.discount, 'price_policy': psi.price_policy, 'package_qty_inv': psi.package_qty, 'uom_po_id': psi.product_uom.id, 'state': 'up_to_date', }) self.qty_available = vals['qty_available'] self.incoming_qty = vals['incoming_qty'] self.outgoing_qty = vals['outgoing_qty'] self.computed_qty = vals['computed_qty'] self.weight = vals['weight'] self.uom_po_id = vals['uom_po_id'] self.product_price_inv = vals['product_price_inv'] self.discount_inv = vals['discount_inv'] self.price_policy = vals['price_policy'] self.package_qty_inv = vals['package_qty_inv'] self.average_consumption = vals['average_consumption'] self.consumption_range = vals['consumption_range'] @api.multi def unlink_psi(self): psi_obj = self.env["product.supplierinfo"] for cpol in self: cpo = cpol.computed_purchase_order_id partner_id = cpo.partner_id.id product_tmpl_id = cpol.product_id.product_tmpl_id.id psi_ids = psi_obj.search([ ('name', '=', partner_id), ('product_tmpl_id', '=', product_tmpl_id)]) psi_ids.unlink() cpol.unlink() @api.multi def create_psi(self): psi_obj = self.env['product.supplierinfo'] for cpol in self: cpo = cpol.computed_purchase_order_id partner_id = cpo.partner_id.id product_tmpl_id = cpol.product_id.product_tmpl_id.id vals = { 'name': partner_id, 'product_name': cpol.product_name, 'product_code': cpol.product_code, 'product_uom': cpol.uom_po_id.id, 'package_qty': cpol.package_qty_inv, 'min_qty': cpol.package_qty, 'product_id': product_tmpl_id, 'pricelist_ids': [(0, 0, { 'min_quantity': 0, 'price': cpol.product_price_inv, 'discount': cpol.discount_inv, })], 'price_policy': cpol.price_policy, } psi_id = psi_obj.create(vals) cpol.state = 'up_to_date' return psi_id
class AccountInvoiceLine(models.Model): _inherit = 'account.invoice.line' @api.one @api.depends('price_unit', 'discount', 'invoice_line_tax_id', 'quantity', 'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id') 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, fiscal_position=self.fiscal_position) self.price_subtotal = taxes['total'] - taxes['total_tax_discount'] self.price_total = taxes['total'] if self.invoice_id: self.price_subtotal = self.invoice_id.currency_id.round( self.price_subtotal) self.price_total = self.invoice_id.currency_id.round( self.price_total) invoice_line_tax_id = fields.Many2many('account.tax', 'account_invoice_line_tax', 'invoice_line_id', 'tax_id', string='Taxes', domain=[('parent_id', '=', False)]) fiscal_category_id = fields.Many2one('l10n_br_account.fiscal.category', 'Categoria Fiscal') fiscal_position = fields.Many2one( 'account.fiscal.position', u'Posição Fiscal', domain="[('fiscal_category_id', '=', fiscal_category_id)]") price_total = fields.Float(string='Amount', store=True, digits=dp.get_precision('Account'), readonly=True, compute='_compute_price') def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False): result = super(AccountInvoiceLine, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu) if context is None: context = {} if view_type == 'form': eview = etree.fromstring(result['arch']) if 'type' in context.keys(): expr = "//field[@name='fiscal_category_id']" fiscal_categories = eview.xpath(expr) for fiscal_category_id in fiscal_categories: fiscal_category_id.set( 'domain', """[('type', '=', '%s'), ('journal_type', '=', '%s')]""" % (OPERATION_TYPE[context['type']], JOURNAL_TYPE[context['type']])) fiscal_category_id.set('required', '1') product_ids = eview.xpath("//field[@name='product_id']") for product_id in product_ids: product_id.set( 'domain', "[('fiscal_type', '=', '%s')]" % (context.get('fiscal_type', 'service'))) result['arch'] = etree.tostring(eview) return result
class jmd_asunto(models.Model): _inherit = "mail.thread" _name = "utils.minuta.asunto" @api.one def task(self): ret = {} self.project_id.write({ 'task_ids': [(0, 0, { 'name': self.descripcion, 'planned_hours': 1, 'user_id': self.responsable.user_id.id, 'date_deadline': self.fecha_limite })] }) self.write({'tarea': True}) return ret @api.one def appoint(self): ret = {} fecha = self.fecha_limite print(type(fecha)) print(fecha) fecha_obj = datetime.strptime(self.fecha_limite, "%Y-%m-%d") fechastr = datetime.strftime(fecha_obj, "%Y-%m-%d") + " 10:00:00" fechastop = datetime.strftime(fecha_obj, "%Y-%m-%d") + " 11:00:00" self.env['calendar.event'].create({ 'name': self.descripcion, 'start_datetime': fechastr, 'stop_datetime': fechastop, }) self.write({'cita': True}) return ret name = fields.Char("Consecutivo") descripcion = fields.Char("Descripción") responsable = fields.Many2one("hr.employee", string="Responsable") fecha_limite = fields.Date("Fecha Límite") vuelta = fields.Integer("Vuelta", default=1) horas_dedicadas = fields.Float("Horas Dedicadas") realizado = fields.Boolean("Realizado") comentarios = fields.Text("Comentarios") prioridad = fields.Selection([("Alta", "Alta"), ("Media", "Media"), ("Baja", "Baja")], string="Prioridad") pasos = fields.Char("Pasos a seguir") tarea = fields.Boolean("Tarea") cita = fields.Boolean("Cita") minuta_id = fields.Many2one("utils.minuta") motivo_cambios = fields.Selection( [('diseno', 'Problemas de Diseño'), ('cliente', 'Problemas de Cliente'), ('construccion', 'Problemas de Construcción')], string="Motivo de los Cambios") project_id = fields.Many2one("project.project", string="Proyecto") adjunto = fields.Binary("Archivo Adjunto") nadjunto = fields.Char("Nombre del Archivo") tipo = fields.Selection([('Proveedor', 'Proveedor'), ('Cliente', 'Cliente')], string="Tipo")
class location_moves(models.TransientModel): _name = 'location.moves' product_id = fields.Many2one('product.product', 'Product', required=True) qty = fields.Float('Qty', required=True) check_qty = fields.Boolean( 'Check Qty', default=lambda self: self.env.context.get('manual', False)) move_type = fields.Selection([ ('beach_stock', 'Beach -> Stock'), ('beach_kitchen', 'Beach -> Kitchen'), ('beach_pantry', 'Beach -> Pantry'), ('stock_kitchen', 'Stock -> Kitchen'), ('stock_pantry', 'Stock -> Pantry'), ('pantry_kitchen', 'Pantry -> Kitchen'), ('kitchen_cooked', 'Kitchen -> Cooked'), ('kitchen_nursing', 'Kitchen -> Nursing'), ('nursing_damaged', 'Nursing -> Damaged'), ('nursing_cooked', 'Nursing -> Cooked'), ('quality_cooked', 'Quality -> Cooked'), ('cooked_quality', 'Cooked -> Quality'), ('cooked_damaged', 'Cooked -> Damaged'), ('marketing_stock', 'Marketing -> Stock'), ('marketing_product', 'Marketing -> Product'), ('stock_marketing', 'Stock -> Marketing'), ('product_stock', 'Product -> Stock'), ('stock_product', 'Stock -> Product'), ('development_stock', 'Development -> Stock'), ('stock_development', 'Stock -> Development'), ('sat_stock', 'SAT -> Stock'), ('stock_sat', 'Stock -> SAT'), ('external_stock', 'External -> Stock'), ('beach_external', 'Beach -> External'), ], 'Move type', required=True) @api.one def create_moves(self): loc_obj = self.env['stock.location'] types = { 'beach_stock': loc_obj.move_beach_stock, 'beach_kitchen': loc_obj.move_beach_kitchen, 'beach_pantry': loc_obj.move_beach_pantry, 'stock_kitchen': loc_obj.move_stock_kitchen, 'stock_pantry': loc_obj.move_stock_pantry, 'pantry_kitchen': loc_obj.move_pantry_kitchen, 'kitchen_cooked': loc_obj.move_kitchen_cooked, 'kitchen_nursing': loc_obj.move_kitchen_nursing, 'nursing_damaged': loc_obj.move_nursing_damaged, 'nursing_cooked': loc_obj.move_nursing_cooked, 'quality_cooked': loc_obj.move_quality_cooked, 'cooked_damaged': loc_obj.move_cooked_damaged, 'marketing_stock': loc_obj.move_marketing_stock, 'stock_marketing': loc_obj.move_stock_marketing, 'product_stock': loc_obj.move_product_stock, 'stock_product': loc_obj.move_stock_product, 'development_stock': loc_obj.move_development_stock, 'stock_development': loc_obj.move_stock_development, 'sat_stock': loc_obj.move_sat_stock, 'stock_sat': loc_obj.move_stock_sat, 'cooked_quality': loc_obj.move_cooked_quality, 'marketing_product': loc_obj.move_marketing_product, 'external_stock': loc_obj.move_external_stock, 'beach_external': loc_obj.move_beach_external } types[self.move_type](self.product_id.id, self.qty, self.check_qty) return True
class StockMove(models.Model): _inherit = 'stock.move' returned_qty = fields.Float()
class recibos(models.Model): _name = 'recibos' _description = u'Recibos' _inherit = ['mail.thread'] _rec_name = 'concepto' _track = { 'concepto': { 'recibos.mt_concepto_change': lambda self, cr, uid, obj, ctx=None: True, }, 'fecha': { 'recibos.mt_fecha_change': lambda self, cr, uid, obj, ctx=None: True, }, 'importe': { 'recibos.mt_importe_change': lambda self, cr, uid, obj, ctx=None: True, }, 'cobrado': { 'recibos.mt_cobrado_change': lambda self, cr, uid, obj, ctx=None: True, }, 'pagado': { 'recibos.mt_pagado_change': lambda self, cr, uid, obj, ctx=None: True, }, } _mail_post_access = 'read' #API VIEJA #_columns = { # 'concepto': fields.char(_(u'Concepto'), size=500, required=True), # 'fecha': fields.date(_(u'Fecha'), required=True), # 'importe': fields.float(_(u'Importe'), required=True), # 'cobrado': fields.float(_(u'Cobrado'), required=True), # 'pagado': fields.boolean(_(u'Pagado')), # 'product_id': fields.many2one('product.product', _(u'Vivienda')), # 'pendiente': fields.function(_get_importe_pendiente, type='float', digits=(12, 2), string=_(u'Pendiente')), #} #_defaults = { # 'cobrado': 0, #} #API NUEVA - odoo8 concepto = fields.Char(string='Concepto', required=True) fecha = fields.Date(string='Fecha', required=True) importe = fields.Float(string='Importe Factura', required=True) cobrado = fields.Float(string='Importe Cobrado', required=True, default=0.0) pagado = fields.Boolean(string='Pagado Propietario', default=False) account_id = fields.Many2one('account.analytic.account', string='Contrato') pendiente = fields.Float(string='Importe Pendiente de Cobrar', store=False, compute='_get_importe_pendiente') account_product = fields.Char(string='Vivienda', store=True, compute='_get_account_product') account_product_owner = fields.Char(string='Propietario', store=False, compute='_get_account_product_owner') account_product_tenant = fields.Char(string='Inquilino', store=False, compute='_get_account_product_tenant') @api.one @api.depends('importe', 'cobrado') def _get_importe_pendiente(self): self.pendiente = self.importe - self.cobrado @api.one @api.depends('account_id') def _get_account_product(self): self.account_product = self.account_id.product.name @api.one @api.depends('account_id') def _get_account_product_owner(self): self.account_product_owner = self.account_id.product.owner.name @api.one @api.depends('account_id') def _get_account_product_tenant(self): self.account_product_tenant = self.account_id.partner_id.name
class Ticket(models.Model): """Add complexity and risk functionnality to tickets Risk is based on complexities. Each complexity has a risk value, and the risk is copied on the ticket """ _inherit = 'anytracker.ticket' @api.depends('rating_ids', 'parent_id', 'child_ids') def _get_my_rating(self): """get my latest rating for this ticket """ RATING = self.env['anytracker.rating'] rating = False for ticket in self: ratings = RATING.search([('user_id', '=', ticket.env.uid), ('ticket_id', '=', ticket.id)], order='time DESC, id DESC') if ratings: rating = ratings[0].complexity_id.id ticket.my_rating = rating def _set_my_rating(self): """set my rating """ # pre-read ratings of the recordset because I found a cache bug # if I replace ratings[ticket.id] with ticket.my_rating.id ratings = {t.id: t.my_rating.id for t in self} RATING = self.env['anytracker.rating'] for ticket in self: RATING.create({ 'complexity_id': ratings[ticket.id], 'ticket_id': ticket.id, 'user_id': ticket.env.uid, 'time': strftime('%Y-%m-%d %H:%M:%S') }) @api.depends('rating_ids', 'my_rating', 'parent_id', 'child_ids') def _get_color(self): """get the color of the rating with highest risk """ for ticket in self: colors = list((r.complexity_id.risk, r.complexity_id) for r in ticket.sudo().rating_ids) if colors: ticket.color = list(reversed(sorted(colors)))[0][1].color else: ticket.color = 0 def compute_risk_and_rating(self, ids): """compute the risk and rating of a leaf ticket, given all its individual ratings """ # TODO: split risk and rating res_risk, res_rating = {}, {} for ticket in self.browse(ids): if ticket.type.has_children: # not a leaf res_risk[ticket.id] = ticket.risk res_rating[ticket.id] = ticket.rating continue latest_person_risk, latest_person_rating = {}, {} # find latest risk and rating for each person for risk in sorted([(r.time, r.id, r.user_id, r.complexity_id.risk) for r in ticket.rating_ids]): latest_person_risk[risk[2]] = risk[-1] for rating in sorted([(r.time, r.id, r.user_id, r.complexity_id.value) for r in ticket.rating_ids]): latest_person_rating[rating[2]] = rating[-1] # a rating or risk of False or None is skipped latest_person_rating = dict([ r for r in latest_person_rating.items() if r[-1] not in (None, False) ]) latest_person_risk = dict([ r for r in latest_person_risk.items() if r[-1] not in (None, False) ]) # compute the mean of all latest ratings res_risk[ticket.id] = (risk_mean(latest_person_risk.values()) if latest_person_risk else 0.5) res_rating[ticket.id] = (sum(latest_person_rating.values()) / len(latest_person_rating) if latest_person_rating else 0) return res_risk, res_rating def recompute_subtickets(self): """recompute the overall risk and rating of the node, based on subtickets. And recompute sub-nodes as well """ for ticket in self: if not ticket.type.has_children: risk, rating = self.compute_risk_and_rating(ticket.id) ticket.write({ 'risk': risk[ticket.id], 'rating': rating[ticket.id] }) else: leafs = self.search([('id', 'child_of', ticket.id), ('type.has_children', '=', False), ('id', '!=', ticket.id)]) leafs.recompute_subtickets() subnodes = self.search([('id', 'child_of', ticket.id), ('type.has_children', '=', True), ('id', '!=', ticket.id)]) for node in ticket + subnodes: leafs = self.search([('id', 'child_of', node.id), ('type.has_children', '=', False), ('id', '!=', node.id)]) rating = sum(leaf.rating for leaf in leafs) risk = risk_mean(leaf.risk for leaf in leafs) node.write({'risk': risk, 'rating': rating}) return True @api.multi def unlink(self): parent_ids = self.parent_id.ids for ticket in self: # check if the old parent had other children if len(ticket.parent_id.child_ids) == 1: ticket.parent_id.write({'rating': 0.0, 'risk': 0.5}) super(Ticket, self).unlink() # recompute the remaining self.search([('id', 'in', parent_ids)]).recompute_parents() def write(self, values): """Climb the tree from the ticket to the root and recompute the risk of parents Unrated tickets have a risk of 0.5 and rating of 0.0!! """ if 'my_rating' in values or 'parent_id' in values: old_values = [{ 'id': t.id, 'parent_id': t.parent_id.id, 'project_id': t.project_id.id } for t in self] res = super(Ticket, self).write(values) if 'my_rating' in values or 'parent_id' in values: # update the rating and risk (may be different for each ticket, # even if my_rating is the same) new_risk, new_rating = self.compute_risk_and_rating(self.ids) for ticket in self: super(Ticket, ticket).write({ 'risk': new_risk[ticket.id], 'rating': new_rating[ticket.id] }) # Propagate to the parents if 'my_rating' in values and 'parent_id' not in values: parents = self.browse(v['parent_id'] for v in old_values) parents.recompute_parents() elif values.get('parent_id'): # We reparented, we recompute the subnodes old_proj_ids = [v['project_id'] for v in old_values] new_proj_ids = self.browse(self.ids).project_id.ids all_projects = self.browse(list(set(old_proj_ids + new_proj_ids))) all_projects.recompute_subtickets() return res def create(self, values): """climb the tree up to the root and recompute """ # ignore False or None ratings at creation in case the UI gives it if 'my_rating' in values and not values['my_rating']: values.pop('my_rating') ticket = super(Ticket, self).create(values) if values.get('my_rating'): new_risk, new_rating = self.compute_risk_and_rating([ticket.id]) super(Ticket, ticket).write({ 'risk': new_risk[ticket.id], 'rating': new_rating[ticket.id] }) if values.get('parent_id'): self.browse(values.get('parent_id')).recompute_parents() return ticket def recompute_parents(self): """climb the tree starting from ids up to the root and recompute the risk and rating of each ticket. The overall rating is the sum of all the rating below. If we have 3 tickets with risks a, b, c, we compute the overall risk as: R3 = 1 - ((1-a)(1-b)-(1-c))^1/3 """ # ticket is the ticket that has been changed or reparented if not self: return for ticket in self: parent = ticket if ticket else None if not parent: continue # loop up to the root while parent: leafs = self.search([('id', 'child_of', parent.id), ('type.has_children', '=', False), ('id', '!=', parent.id)]) if leafs: rating = sum(leaf.rating for leaf in leafs) risk = risk_mean(leaf.risk for leaf in leafs) parent.write({'risk': risk, 'rating': rating}) parent = parent.parent_id rating_ids = fields.One2many('anytracker.rating', 'ticket_id', 'Ratings') my_rating = fields.Many2one('anytracker.complexity', compute=_get_my_rating, inverse=_set_my_rating, string="My Rating") risk = fields.Float('Risk', group_operator="avg", default=0.5) color = fields.Integer(compute=_get_color, string='Color') rating = fields.Float('Rating', group_operator="sum")
class Milibro2(models.Model): _inherit = 'milibro' isbn = fields.Char('ISBN', size=15, required=True) preu = fields.Float('Preu', (3, 2)) resum = fields.Text('Resum') data = fields.Date('Fecha')
class ObservationComponent(models.Model): _name = "hc.observation.component" _description = "Observation Component" _inherit = ["hc.backbone.element"] observation_id = fields.Many2one( comodel_name="hc.res.observation", string="Observation", required="True", help="Observation associated with this Observation Component.") code = fields.Float(string="Code", required="True", help="Type of component observation (code / type).") value_type = fields.Selection(string="Component Value Type", selection=[("quantity", "Quantity"), ("code", "Code"), ("string", "String"), ("range", "Range"), ("ratio", "Ratio"), ("sampled_data", "Sampled Data"), ("attachment", "Attachment"), ("time", "Time"), ("date_time", "Date Time"), ("period", "Period")], help="Type of result.") value_name = fields.Char(string="Value", compute="_compute_value_name", help="Actual result.") value_quantity = fields.Float(string="Value Quantity", help="Quantity actual result.") value_quantity_uom_id = fields.Many2one( comodel_name="product.uom", string="Value Quantity UOM", help="Value quantity unit of measure.") value_code_id = fields.Many2one( comodel_name="hc.vs.observation.value.code", string="Value Code", help="Code of actual result.") value_string = fields.Char(string="Value", help="String of actual result.") value_range_low = fields.Float(string="Value Range Low", help="Low limit of actual result.") value_range_high = fields.Float(string="Value Range High", help="High limit of actual result.") value_numerator = fields.Float(string="Value Numerator", help="Numerator value of actual result.") value_numerator_uom_id = fields.Many2one( comodel_name="product.uom", string="Value Numerator UOM", help="Value numerator unit of measure.") value_denominator = fields.Float( string="Value Denominator", help="Denominator value of actual result.") value_denominator_uom_id = fields.Many2one( comodel_name="product.uom", string="Value Denominator UOM", help="Value denominator unit of measure.") value_ratio = fields.Float(string="Value Ratio", compute="_compute_value_ratio", store="True", help="Ratio of actual result.") value_ratio_uom = fields.Char(string="Value Ratio UOM", compute="_compute_value_ratio_uom", store="True", help="Value Ratio unit of measure.") value_component = fields.Float(string="Value Component", compute="_compute_value_component", store="True", help="Actual result.") value_component_uom_id = fields.Many2one( comodel_name="product.uom", string="Value Component UOM", help="Actual result unit of measure.") value_sampled_data_id = fields.Many2one( comodel_name="hc.observation.component.value.sampled.data", string="Value Sampled Data", help="Sampled Data actual result.") value_attachment_id = fields.Many2one( comodel_name="hc.observation.component.value.attachment", string="Value Attachment", help="Attachment actual result.") value_time = fields.Char(string="Value Time", help="Time actual result.") value_date_time = fields.Datetime(string="Value Date Time", help="Date Time actual result.") value_start_date = fields.Datetime(string="Value Start Date", help="Start of the actual result.") value_end_date = fields.Datetime(string="Value End Date", help="End of the actual result.") is_data_absent = fields.Boolean(string="Data Absent", help="Result is missing?") data_absent_reason_id = fields.Many2one( comodel_name="hc.vs.observation.value.absent.reason", string="Data Absent Reason", help="Why the result is missing.") interpretation_id = fields.Many2one( comodel_name="hc.vs.observation.interpretation", string="Interpretation", help="High, low, normal, etc.") reference_range_ids = fields.One2many( comodel_name="hc.observation.reference.range", inverse_name="component_id", string="Reference Ranges", help="Provides guide for interpretation.") # Extension attribute modifier_extension_ids = fields.One2many( comodel_name="hc.observation.component.modifier.extension", inverse_name="component_id", string="Modifier Extensions", help="Extensions that cannot be ignored.") value_quantity_id = fields.Many2one( comodel_name="hc.observation.component.value.quantity", string="Value Quantity", required="True", help="Value Quantity associated with this Component.") @api.multi def _compute_value_name(self): for hc_observation_component in self: if hc_observation_component.value_type == 'quantity': hc_observation_component.value_name = str( hc_observation_component.value_quantity) elif hc_observation_component.value_type == 'code': hc_observation_component.value_name = hc_observation_component.value_codeable_concept_id.name elif hc_observation_component.value_type == 'string': hc_observation_component.value_name = hc_observation_component.value_string elif hc_observation_component.value_type == 'range': hc_observation_component.value_name = "Between " + str( hc_observation_component.value_range_low) + " and " + str( hc_observation_component.value_range_high) elif hc_observation_component.value_type == 'ratio': hc_observation_component.value_name = str( hc_observation_component.value_ratio) + " " + str( hc_observation_component.value_ratio_uom) elif hc_observation_component.value_type == 'sampled_data': hc_observation_component.value_name = hc_observation_component.value_sampled_data_id.name elif hc_observation_component.value_type == 'attachment': hc_observation_component.value_name = hc_observation_component.value_attachment_id.name elif hc_observation_component.value_type == 'time': hc_observation_component.value_name = hc_observation_component.value_time elif hc_observation_component.value_type == 'date_time': hc_observation_component.value_name = str( hc_observation_component.value_date_time) elif hc_observation_component.value_type == 'period': hc_observation_component.value_name = "Between " + str( hc_observation_component.value_start_date) + " and " + str( hc_observation_component.value_end_date)
class ImLivechatReportChannel(models.Model): """ Livechat Support Report on the Channels """ _name = "im_livechat.report.channel" _description = "Livechat Support Report" _order = 'start_date, technical_name' _auto = False uuid = fields.Char('UUID', readonly=True) channel_id = fields.Many2one('mail.channel', 'Conversation', readonly=True) channel_name = fields.Char('Channel Name', readonly=True) technical_name = fields.Char('Code', readonly=True) livechat_channel_id = fields.Many2one('im_livechat.channel', 'Channel', readonly=True) start_date = fields.Datetime('Start Date of session', readonly=True, help="Start date of the conversation") start_date_hour = fields.Char('Hour of start Date of session', readonly=True) duration = fields.Float('Average duration', digits=(16, 2), readonly=True, group_operator="avg", help="Duration of the conversation (in seconds)") nbr_speaker = fields.Integer('# of speakers', readonly=True, group_operator="avg", help="Number of different speakers") nbr_message = fields.Integer('Average message', readonly=True, group_operator="avg", help="Number of message in the conversation") partner_id = fields.Many2one('res.partner', 'Operator', readonly=True) def init(self, cr): # Note : start_date_hour must be remove when the read_group will allow grouping on the hour of a datetime. Don't forget to change the view ! tools.drop_view_if_exists(cr, 'im_livechat_report_channel') cr.execute(""" CREATE OR REPLACE VIEW im_livechat_report_channel AS ( SELECT C.id as id, C.uuid as uuid, C.id as channel_id, C.name as channel_name, CONCAT(L.name, ' / ', C.id) as technical_name, C.livechat_channel_id as livechat_channel_id, C.create_date as start_date, to_char(date_trunc('hour', C.create_date), 'YYYY-MM-DD HH24:MI:SS') as start_date_hour, EXTRACT('epoch' FROM (max((SELECT (max(M.create_date)) FROM mail_message M JOIN mail_message_mail_channel_rel R ON (R.mail_message_id = M.id) WHERE R.mail_channel_id = C.id))-C.create_date)) as duration, count(distinct P.id) as nbr_speaker, count(distinct M.id) as nbr_message, MAX(S.partner_id) as partner_id FROM mail_channel C JOIN mail_message_mail_channel_rel R ON (C.id = R.mail_channel_id) JOIN mail_message M ON (M.id = R.mail_message_id) JOIN mail_channel_partner S ON (S.channel_id = C.id) JOIN im_livechat_channel L ON (L.id = C.livechat_channel_id) LEFT JOIN res_partner P ON (M.author_id = P.id) GROUP BY C.id, C.name, C.livechat_channel_id, L.name, C.create_date, C.uuid ) """)
class Observation(models.Model): _name = "hc.res.observation" _description = "Observation" _inherit = ["hc.domain.resource"] _rec_name = "name" name = fields.Char( string="Event Name", required="True", help= "Text representation of the observation event. Subject Name + Code + Effective Date." ) identifier_ids = fields.One2many( comodel_name="hc.observation.identifier", inverse_name="observation_id", string="Identifiers", help="Unique Id for this particular observation.") based_on_ids = fields.One2many(comodel_name="hc.observation.based.on", inverse_name="observation_id", string="Based On", help="Fulfills plan, proposal or order.") status = fields.Selection(string="Status", selection=[("registered", "Registered"), ("preliminary", "Preliminary"), ("final", "Final"), ("amended", "Amended")], help="The status of the result value.") category_ids = fields.Many2many( comodel_name="hc.vs.observation.category", string="Categories", help="Classification of type of observation.") code_id = fields.Many2one(comodel_name="hc.vs.observation.code", string="Code", required="True", help="Type of observation (code / type).") subject_type = fields.Selection( string="Observation Subject Type", selection=[("patient", "Patient"), ("group", "Group"), ("device", "Device"), ("location", "Location")], help="Type of who and/or what this is about.") subject_name = fields.Char(string="Subject", compute="_compute_subject_name", help="Who and/or what this is about.") subject_patient_id = fields.Many2one( comodel_name="hc.res.patient", string="Subject Patient", help="Patient who and/or what this is about.") subject_group_id = fields.Many2one( comodel_name="hc.res.group", string="Subject Group", help="Group who and/or what this is about.") subject_device_id = fields.Many2one( comodel_name="hc.res.device", string="Subject Device", help="Device who and/or what this is about.") subject_location_id = fields.Many2one( comodel_name="hc.res.location", string="Subject Location", help="Location who and/or what this is about.") context_type = fields.Selection( tring="Context Type", selection=[("encounter", "Encounter"), ("episode_of_care", "Episode Of Care")], help="Healthcare event during which this observation is made.") context_name = fields.Char( string="Context", compute="_compute_context_name", store="True", help="Healthcare event during which this observation is made.") context_encounter_id = fields.Many2one( comodel_name="hc.res.encounter", string="Context Encounter", help="Encounter during which this observation is made.") context_episode_of_care_id = fields.Many2one( comodel_name="hc.res.episode.of.care", string="Context Episode Of Care", help="Episode Of Care during which this observation is made.") effective_type = fields.Selection( string="Effective Date Type", selection=[("date_time", "Datetime"), ("period", "Period")], help="Type of Clinically relevant time/time-period for observation.") effective_name = fields.Char(string="Effective Date", compute="_compute_effective_name", help="Clinically relevant time/time.") effective_date_time = fields.Datetime( string="Effective Datetime", help="Date time clinically relevant time/time-period for observation.") effective_start_date = fields.Datetime( string="Effective Start Date", help= "Start of the clinically relevant time/time-period for observation.") effective_end_date = fields.Datetime( string="Effective End Date", help="End of the clinically relevant time/time-period for observation." ) issued_date = fields.Datetime(string="Issued Date Date", help="Date/Time this was made available.") performer_ids = fields.One2many( comodel_name="hc.observation.performer", inverse_name="observation_id", string="Performers", help="Who is responsible for the observation.") value_type = fields.Selection(string="Observation Value Type", selection=[("quantity", "Quantity"), ("code", "Code"), ("string", "String"), ("range", "Range"), ("ratio", "Ratio"), ("sampled_data", "Sampled Data"), ("attachment", "Attachment"), ("time", "Time"), ("date_time", "Date Time"), ("period", "Period")], help="Type of result.") value_name = fields.Char(string="Value", compute="_compute_value_name", help="Actual result.") value_quantity = fields.Float(string="Value Quantity", help="Quantity actual result.") value_quantity_uom_id = fields.Many2one( comodel_name="product.uom", string="Value Quantity UOM", help="Value quantity unit of measure.") value_code_id = fields.Many2one( comodel_name="hc.vs.observation.value.code", string="Value Code", help="Code of actual result.") value_string = fields.Char(string="Value", help="String of actual result.") value_range_low = fields.Float(string="Value Range Low", help="Low limit of actual result.") value_range_high = fields.Float(string="Value Range High", help="High limit of actual result.") value_ratio_numerator = fields.Float( string="Value Ratio Numerator", help="Numerator value of actual result.") value_ratio_numerator_uom_id = fields.Many2one( comodel_name="product.uom", string="Value Ratio Numerator UOM", help="Value numerator unit of measure.") value_ratio_denominator = fields.Float( string="Value Ratio Denominator", help="Denominator value of actual result.") value_ratio_denominator_uom_id = fields.Many2one( comodel_name="product.uom", string="Value Ratio Denominator UOM", help="Value denominator unit of measure.") value_ratio = fields.Float(string="Value Ratio", compute="_compute_value_ratio", store="True", help="Ratio of actual result.") value_ratio_uom = fields.Char(string="Value Ratio UOM", compute="_compute_value_ratio_uom", store="True", help="Value Ratio unit of measure.") value_ratio_name = fields.Char( string="Value Ratio", compute="_compute_value_ratio_name", store="True", help="Numerator + Numerator UOM / Denominator + Denominator UOM.") value_sampled_data_id = fields.Many2one( comodel_name="hc.observation.value.sampled.data", string="Value Sampled Data", help="Sampled Data actual result.") value_attachment_id = fields.Many2one( comodel_name="hc.observation.value.attachment", string="Value Attachment", help="Attachment actual result.") value_time = fields.Char(string="Value Time", help="Time actual result.") value_date_time = fields.Datetime(string="Value Date Time", help="Date Time actual result.") value_start_date = fields.Datetime(string="Value Start Date", help="Start of the actual result.") value_end_date = fields.Datetime(string="Value End Date", help="End of the actual result.") is_data_absent = fields.Boolean(string="Data Absent", help="Result is missing?") data_absent_reason_id = fields.Many2one( comodel_name="hc.vs.observation.value.absent.reason", string="Data Absent Reason", help="Why the result is missing.") interpretation_id = fields.Many2one( comodel_name="hc.vs.observation.interpretation", string="Interpretation", help="High, low, normal, etc.") comment = fields.Text(string="Comment", help="Comments about result.") body_site_id = fields.Many2one(comodel_name="hc.vs.body.site", string="Body Site", help="Observed body part") method_id = fields.Many2one(comodel_name="hc.vs.observation.method", string="Method", help="How it was done.") specimen_id = fields.Many2one(comodel_name="hc.res.specimen", string="Specimen", help="Specimen used for this observation.") device_type = fields.Selection(string="Observation Device Type", selection=[("device", "Device"), ("device_metric", "Device Metric")], help="Type of device.") device_name = fields.Char(string="Device", compute="_compute_device_name", help="(Measurement) Device.") device_id = fields.Many2one(comodel_name="hc.res.device", string="Device", help="Device (measurement) device.") device_metric_id = fields.Many2one( comodel_name="hc.res.device.metric", string="Device Metric", help="Device Metric (measurement) device.") reference_range_ids = fields.One2many( comodel_name="hc.observation.reference.range", inverse_name="observation_id", string="Reference Ranges", help="Provides guide for interpretation.") related_ids = fields.One2many( comodel_name="hc.observation.related", inverse_name="observation_id", string="Related", help="Observations related to this observation.") component_ids = fields.One2many(comodel_name="hc.observation.component", inverse_name="observation_id", string="Components", help="Component results.") # Extension Attribute id = fields.Char(string="Id", help="Logical id of this artifact.") meta_id = fields.Many2one(comodel_name="hc.observation.meta", string="Meta", help="Metadata about the resource.") implicit_rules = fields.Char( string="Implicit Rules URI", help="A set of rules under which this content was created.") language_id = fields.Many2one(comodel_name="res.lang", string="Language", help="of the resource content.") text_id = fields.Many2one( comodel_name="hc.observation.text", string="Text", help="Text summary of the resource, for human interpretation.") contained_ids = fields.One2many(comodel_name="hc.observation.contained", inverse_name="observation_id", string="Contained", help="Contained, inline Resources.") modifier_extension_ids = fields.One2many( comodel_name="hc.observation.modifier.extension", inverse_name="observation_id", string="Modifier Extensions", help="Extensions that cannot be ignored.") # technical attribute has_value_ratio_numerator = fields.Boolean( string="Has Value Ratio Numerator", invisible=True, help= "Indicates if value_ratio_numerator exists. Used to enforce constraint value_ratio_numerator and value_ratio_denominator." ) _sql_constraints = [ ('value_ratio_numerator_gt_zero', 'CHECK(value_ratio_numerator >= 0.0)', 'Value Ratio Numerator SHALL be a non-negative value.'), ('value_ratio_denominator_gt_zero', 'CHECK(value_ratio_denominator >= 0.0)', 'Value Ratio Denominator SHALL be a non-negative value.') ] @api.multi def _compute_subject_name(self): for hc_res_observation in self: if hc_res_observation.subject_type == 'patient': hc_res_observation.subject_name = hc_res_observation.subject_patient_id.name elif hc_res_observation.subject_type == 'group': hc_res_observation.subject_name = hc_res_observation.subject_group_id.name elif hc_res_observation.subject_type == 'device': hc_res_observation.subject_name = hc_res_observation.subject_device_id.name elif hc_res_observation.subject_type == 'location': hc_res_observation.subject_name = hc_res_observation.subject_location_id.name @api.multi def _compute_effective_name(self): for hc_res_observation in self: if hc_res_observation.effective_type == 'date_time': hc_res_observation.effective_name = str( hc_res_observation.effective_date_time) elif hc_res_observation.effective_type == 'period': hc_res_observation.effective_name = "Between " + str( hc_res_observation.effective_start_date) + " and " + str( hc_res_observation.effective_end_date) @api.multi def _compute_value_name(self): for hc_res_observation in self: if hc_res_observation.value_type == 'quantity': hc_res_observation.value_name = str( hc_res_observation.value_quantity) elif hc_res_observation.value_type == 'code': hc_res_observation.value_name = hc_res_observation.value_codeable_concept_id.name elif hc_res_observation.value_type == 'string': hc_res_observation.value_name = hc_res_observation.value_string elif hc_res_observation.value_type == 'range': hc_res_observation.value_name = "Between " + str( hc_res_observation.value_range_low) + " and " + str( hc_res_observation.value_range_high) elif hc_res_observation.value_type == 'ratio': hc_res_observation.value_name = str( hc_res_observation.value_ratio) + " " + str( hc_res_observation_.value_ratio_uom) elif hc_res_observation.value_type == 'sampled_data': hc_res_observation.value_name = hc_res_observation.value_sampled_data_id.name elif hc_res_observation.value_type == 'attachment': hc_res_observation.value_name = hc_res_observation.value_attachment_id.name elif hc_res_observation.value_type == 'time': hc_res_observation.value_name = hc_res_observation.value_time elif hc_res_observation.value_type == 'date_time': hc_res_observation.value_name = str( hc_res_observation.value_date_time) elif hc_res_observation.value_type == 'period': hc_res_observation.value_name = "Between " + str( hc_res_observation.value_start_date) + " and " + str( hc_res_observation.value_end_date) @api.multi def _compute_device_name(self): for hc_res_observation in self: if hc_res_observation.device_type == 'device': hc_res_observation.device_name = hc_res_observation.device_device_id.name elif hc_res_observation.device_type == 'device_metric': hc_res_observation.device_name = hc_res_observation.device_device_metric_id.name @api.onchange('value_ratio_numerator') def _onchange_value_ratio_numerator(self): if self.value_ratio_numerator: self.has_value_ratio_numerator = True else: self.has_value_ratio_numerator = False @api.depends('value_ratio_numerator', 'value_ratio_denominator') def _compute_value_ratio(self): if self.value_ratio_numerator and self.value_ratio_denominator: self.value_ratio = self.value_ratio_numerator / self.value_ratio_denominator @api.depends('value_ratio_numerator_uom_id', 'value_ratio_denominator_uom_id') def _compute_value_ratio_uom(self): value_ratio_uom = '/' if self.value_ratio_numerator_uom_id: value_ratio_uom = self.value_ratio_numerator_uom_id.code if self.value_ratio_denominator_uom_id: value_ratio_uom = value_ratio_uom + ' per ' + self.value_ratio_denominator_uom_id.code self.value_ratio_uom = value_ratio_uom @api.depends('value_ratio_numerator', 'value_ratio_denominator', 'value_ratio_numerator_uom_id', 'value_ratio_denominator_uom_id') def _compute_value_ratio_name(self): value_ratio_name = '/' if self.value_ratio_numerator and self.value_ratio_denominator: self.value_ratio_name = str( self.value_ratio_numerator) + ' ' + str( self.value_ratio_numerator_uom_id.code) + '/' + str( self.value_ratio_denominator) + ' ' + str( self.value_ratio_denominator_uom_id.code)
class AccountInvoice(models.Model): _inherit = "account.invoice" amount_expense_request = fields.Float( string='Expense Request Amount', copy=False, readonly=True, ) diff_expense_amount_flag = fields.Integer( string='Amount different from expense', compute='_compute_diff_expense_amount_flag', readonly=True, default=0, help=""" * Flat = 0 when amount invoice = amount expense\n * Flag = 1 when amount invoice > amount expense.\n * Flag = -1 when amount invoice < amount expense. """) diff_expense_amount_reason = fields.Char( string='Amount Diff Reason', readonly=True, states={'draft': [('readonly', False)]}, help="Reason when amount is different from Expense", ) @api.multi @api.depends('amount_total') def _compute_diff_expense_amount_flag(self): for rec in self: if rec.expense_id: rec.diff_expense_amount_flag = 0 clear_amount = sum([ x.price_subtotal < 0.0 and x.price_subtotal or 0.0 for x in rec.invoice_line ]) amount = rec.amount_total - clear_amount rec.diff_expense_amount_flag = \ float_compare(amount, rec.amount_expense_request, precision_digits=1) @api.onchange('advance_expense_id') def _onchange_advance_expense_id(self): super(AccountInvoice, self)._onchange_advance_expense_id() if len(self.taxbranch_ids) == 1: self.taxbranch_id = self.taxbranch_ids[0].id @api.multi def confirm_paid(self): expenses = self.env['hr.expense.expense'].search([('invoice_id', 'in', self._ids)]) History = self.env['hr.expense.advance.due.history'] Voucher = self.env['account.voucher'] for expense in expenses: if not expense.is_employee_advance: continue date_due = False # Case 1) buy_product, date_due = date_value + 30 days if expense.advance_type == 'buy_product': move_ids = \ expense.invoice_id.payment_ids.mapped('move_id')._ids vouchers = Voucher.search([('move_id', 'in', move_ids)], order='date desc', limit=1) date_paid = vouchers[0].date_value date_due = (datetime.strptime(date_paid, '%Y-%m-%d') + relativedelta(days=30)) # Case 2) attend_seminar, date_due = arrive date + 30 days elif expense.advance_type == 'attend_seminar': date_back = expense.date_back date_due = (datetime.strptime(date_back, '%Y-%m-%d') + relativedelta(days=30)) else: raise ValidationError( _('Can not calculate due date. ' 'No Advance Type vs Due Date Rule')) if date_due: History.create({ 'expense_id': expense.id, 'date_due': date_due.strftime('%Y-%m-%d'), }) return super(AccountInvoice, self).confirm_paid() @api.multi def action_cancel(self): for invoice in self: if invoice.expense_id: expense = invoice.expense_id if expense.state == 'paid': expense.signal_workflow('invoice_except') elif expense.state == 'done': expense.signal_workflow('done_to_except') return super(AccountInvoice, self).action_cancel() @api.multi def action_move_create(self): result = super(AccountInvoice, self).action_move_create() for invoice in self: if invoice.diff_expense_amount_flag == 1: raise ValidationError( _('New amount over expense is not allowed!')) if invoice.diff_expense_amount_flag == -1 and \ not invoice.diff_expense_amount_reason: raise ValidationError( _('Total amount is changed from Expense Request Amount.\n' 'Please provide Amount Diff Reason')) return result
class ContractType(models.Model): _name = 'contract.type' name = fields.Char() hours = fields.Float()