class AccountBankPayment(models.Model): _inherit = 'account.bank.payment' multiple_reconcile_ids = fields.One2many( 'account.bank.payment.multiple.reconcile', 'bank_payment_id', string='Reconcile Lines', readonly=True, states={'draft': [('readonly', False)]}, ) writeoff_amount = fields.Float( string="Writeoff Amount", readonly=True, compute="_compute_writeoff_amount", help="Computed as the difference between the amount\ stated in the voucher and the sum of allocation\ on the voucher lines.", ) payment_difference_amount = fields.Float( string="Difference Amount", readonly=True, compute="_compute_writeoff_amount", help="Computed as the difference between the amount\ stated in the voucher and the sum of allocation\ on the voucher lines.", ) total_amount = fields.Float( string="Computed Total Amount", copy=False, ) manual_total_amount = fields.Float( string="Total Amount", readonly=True, default=0.0, states={'draft': [('readonly', False)]}, required=True, copy=False, ) move_id = fields.Many2one( 'account.move', string='Journal Entry', readonly=True, copy=False, ) @api.multi def validate_bank_payment(self): for payment in self: if float_compare(payment.writeoff_amount, 0.0, 2) != 0: raise ValidationError( _('Writeoff Amount must be 0.0 to validate!')) res = super(AccountBankPayment, self).validate_bank_payment() return res @api.multi @api.depends('manual_total_amount') def _compute_bank_payment(self): res = super(AccountBankPayment, self)._compute_bank_payment() for payment in self: payment.total_amount = payment.manual_total_amount return res @api.multi @api.depends('total_amount', 'multiple_reconcile_ids', 'multiple_reconcile_ids.amount') def _compute_writeoff_amount(self): for payment in self: total = 0.0 currency_none_same_company_id = False if payment.company_id.currency_id != payment.currency_id: currency_none_same_company_id = payment.currency_id.id for line in payment.bank_intransit_ids: if currency_none_same_company_id: total += line.amount_currency else: total += line.credit writeoffline_amount = 0.0 if payment.multiple_reconcile_ids: writeoffline_amount =\ sum([l.amount for l in payment.multiple_reconcile_ids]) payment.payment_difference_amount = payment.total_amount - total payment.writeoff_amount = (payment.total_amount - writeoffline_amount - total) @api.model def _create_writeoff_move_line_hook(self, move): super(AccountBankPayment, self)._create_writeoff_move_line_hook(move) if self.payment_difference_amount != 0.0: MoveLine = self.env['account.move.line'] if self.payment_difference_amount != 0.0 and \ self.multiple_reconcile_ids: for writeofline in self.multiple_reconcile_ids: move_line_val = \ self._prepare_writeoff_move_line(writeofline) move_line_val['move_id'] = move.id MoveLine.create(move_line_val) return True @api.model def _do_reconcile(self, to_reconcile_lines): if self.payment_difference_amount != 0.0 and \ self.multiple_reconcile_ids: for reconcile_lines in to_reconcile_lines: reconcile_lines.reconcile_partial(type='manual') return True else: return super(AccountBankPayment, self)._do_reconcile(to_reconcile_lines) @api.onchange('writeoff_amount') def onchange_writeoff_amount(self): for payment in self: if payment.writeoff_amount == 0.0: payment.multiple_reconcile_ids = [(6, 0, [])] @api.model def _prepare_counterpart_move_lines_vals(self, payment, total_credit, total_amount_currency): vals = super(AccountBankPayment, self).\ _prepare_counterpart_move_lines_vals(payment, total_credit, total_amount_currency) if self.payment_difference_amount != 0.0 and \ self.multiple_reconcile_ids: vals['credit'] = self.total_amount return vals @api.model def _prepare_writeoff_move_line(self, writeofline): credit = 0.0 debit = 0.0 if writeofline.amount > 0.0: debit = abs(writeofline.amount) else: credit = abs(writeofline.amount) return { 'name': writeofline.note, 'credit': credit, 'debit': debit, 'account_id': writeofline.account_id.id, 'partner_id': False, 'currency_id': False, 'amount_currency': False, 'analytic_account_id': writeofline.analytic_id and writeofline.analytic_id.id or False, }
class is_cout(models.Model): _name='is.cout' _order='name' _sql_constraints = [('name_uniq', 'unique(name)', u"Ce code existe déja !")] name = fields.Many2one('product.product', 'Article', required=True, readonly=False, select=True) code_pg = fields.Char('Code PG' , related='name.product_tmpl_id.is_code', readonly=True) designation = fields.Char('Désignation', related='name.product_tmpl_id.name' , readonly=True) cout_calcul_id = fields.Many2one('is.cout.calcul', 'Calcul des coût') is_category_id = fields.Many2one('is.category', 'Catégorie') is_gestionnaire_id = fields.Many2one('is.gestionnaire', 'Gestionnaire') is_mold_id = fields.Many2one('is.mold', 'Moule') is_mold_dossierf = fields.Char('Moule ou Dossier F') partner_id = fields.Many2one('res.partner', 'Client par défaut') type_article = fields.Selection([('A', u'Acheté'),('F', u'Fabriqué'),('ST', u'Sous-traité')], "Type d'article") uom_id = fields.Many2one('product.uom', 'Unité') lot_mini = fields.Float("Lot d'appro.") prix_tarif = fields.Float("Prix tarif" , digits=(12, 4)) prix_commande = fields.Float("Prix dernière commande" , digits=(12, 4)) prix_facture = fields.Float("Prix dernière facture" , digits=(12, 4)) prix_force = fields.Float("Prix forcé (saisie manuelle)", digits=(12, 4)) prix_force_commentaire = fields.Char("Commentaire") prix_calcule = fields.Float("Prix calculé" , digits=(12, 4)) prix_sous_traitance = fields.Float("Prix sous-traitance" , digits=(12, 4)) ecart_calcule_matiere = fields.Float("Ecart Calculé/Matière" , digits=(12, 4)) cout_act_matiere = fields.Float("Coût act matière" , digits=(12, 4)) cout_act_condition = fields.Float("Coût act conditionnement", digits=(12, 4)) cout_act_machine = fields.Float("Coût act machine" , digits=(12, 4)) cout_act_mo = fields.Float("Coût act main d'oeuvre" , digits=(12, 4)) cout_act_machine_pk = fields.Float("Coût act machine PK" , digits=(12, 4)) cout_act_mo_pk = fields.Float("Coût act main d'oeuvre PK", digits=(12, 4)) cout_act_st = fields.Float("Coût act sous-traitance" , digits=(12, 4)) cout_act_total = fields.Float("Coût act Total" , digits=(12, 4)) cout_act_prix_vente = fields.Float("Prix de vente actualisé" , digits=(12, 4)) cout_std_matiere = fields.Float("Coût std matière" , digits=(12, 4)) cout_std_condition = fields.Float("Coût std conditionnement" , digits=(12, 4)) cout_std_machine = fields.Float("Coût std machine" , digits=(12, 4)) cout_std_mo = fields.Float("Coût std main d'oeuvre" , digits=(12, 4)) cout_std_st = fields.Float("Coût std sous-traitance" , digits=(12, 4)) cout_std_total = fields.Float("Coût std Total" , digits=(12, 4)) cout_std_prix_vente = fields.Float("Prix de vente standard" , digits=(12, 4)) cout_budget_matiere = fields.Float("Coût budget matière" , digits=(12, 4)) cout_budget_condition = fields.Float("Coût budget conditionnement" , digits=(12, 4)) cout_budget_machine = fields.Float("Coût budget machine" , digits=(12, 4)) cout_budget_mo = fields.Float("Coût budget main d'oeuvre" , digits=(12, 4)) cout_budget_st = fields.Float("Coût budget sous-traitance" , digits=(12, 4)) cout_budget_total = fields.Float("Coût budget Total" , digits=(12, 4)) cout_budget_prix_vente = fields.Float("Prix de vente budget" , digits=(12, 4)) amortissement_moule = fields.Float("Amortissement Moule" , digits=(12, 4), compute='_compute') surcout_pre_serie = fields.Float("Surcôut pré-série" , digits=(12, 4), compute='_compute') prix_vente = fields.Float("Prix de Vente" , digits=(12, 4), compute='_compute') nomenclature_ids = fields.One2many('is.cout.nomenclature', 'cout_id', u"Lignes de la nomenclature") gamme_ma_ids = fields.One2many('is.cout.gamme.ma' , 'cout_id', u"Lignes gamme machine") gamme_mo_ids = fields.One2many('is.cout.gamme.mo' , 'cout_id', u"Lignes gamme MO") gamme_ma_pk_ids = fields.One2many('is.cout.gamme.ma.pk' , 'cout_id', u"Lignes gamme machine PK") gamme_mo_pk_ids = fields.One2many('is.cout.gamme.mo.pk' , 'cout_id', u"Lignes gamme MO PK") niveau = fields.Integer('Niveau le plus bas dans la nomenclature') nb_err = fields.Integer('Nb Err', help=u"Nombre d'erreures détectées lors du calcul de coûts") @api.depends('name') def _compute(self): for obj in self: #** Recherche du tarif commercial pour le client par défaut ******** code_client=mem_code_client=False for client in obj.name.product_tmpl_id.is_client_ids: mem_code_client=client.client_id.is_code if client.client_defaut: code_client=mem_code_client if code_client==False: code_client=mem_code_client tarifs=self.env['is.tarif.cial'].search([ ('product_id', '=', obj.name.product_tmpl_id.id), ('indice_prix', '=', 999), ('partner_id.is_code', '=', code_client) ]) for tarif in tarifs: obj.amortissement_moule = tarif.amortissement_moule obj.surcout_pre_serie = tarif.surcout_pre_serie obj.prix_vente = tarif.prix_vente @api.multi def write(self, vals): for obj in self: matiere = vals.get('cout_std_matiere' , obj.cout_std_matiere) machine = vals.get('cout_std_machine' , obj.cout_std_machine) mo = vals.get('cout_std_mo' , obj.cout_std_mo) st = vals.get('cout_std_st' , obj.cout_std_st) vals['cout_std_total']=matiere+machine+mo+st res=super(is_cout, self).write(vals) return res @api.multi def action_calcul_cout(self): for obj in self: vals={ 'product_id' : obj.name.id, 'multiniveaux' : False, } cout_calcul=self.env['is.cout.calcul'].create(vals) cout_calcul.action_calcul_prix_achat_thread(nb_threads=0) cout_calcul.action_calcul_prix_revient() return { 'name': obj.name.name, 'view_mode': 'form', 'view_type': 'form', 'res_model': 'is.cout', 'type': 'ir.actions.act_window', 'res_id': obj.id, } @api.multi def action_calcul_cout_pk(self): for obj in self: vals={ 'product_id' : obj.name.id, 'multiniveaux' : False, } cout_calcul=self.env['is.cout.calcul'].create(vals) cout_calcul.action_calcul_prix_achat_thread(nb_threads=0) cout_calcul.action_calcul_prix_revient() dummy, view_id = self.env['ir.model.data'].get_object_reference('is_plastigray', 'is_cout_pk_form_view') return { 'name': obj.name.name, 'view_mode': 'form', 'view_type': 'form', 'res_model': 'is.cout', 'type': 'ir.actions.act_window', 'view_id': view_id, 'res_id': obj.id, } @api.multi def _copie_cout_actualise_dans_cout_standard(self,obj): vals={ 'cout_std_matiere' : obj.cout_act_matiere, 'cout_std_condition' : obj.cout_act_condition, 'cout_std_machine' : obj.cout_act_machine, 'cout_std_mo' : obj.cout_act_mo, 'cout_std_st' : obj.cout_act_st, 'cout_std_total' : obj.cout_act_total, 'cout_std_prix_vente' : obj.cout_act_prix_vente, } obj.write(vals) @api.multi def copie_cout_actualise_dans_cout_standard(self): for obj in self: self._copie_cout_actualise_dans_cout_standard(obj) @api.multi def _copie_cout_actualise_dans_cout_budget(self,obj): vals={ 'cout_budget_matiere' : obj.cout_act_matiere, 'cout_budget_condition' : obj.cout_act_condition, 'cout_budget_machine' : obj.cout_act_machine, 'cout_budget_mo' : obj.cout_act_mo, 'cout_budget_st' : obj.cout_act_st, 'cout_budget_total' : obj.cout_act_total, 'cout_budget_prix_vente' : obj.cout_act_prix_vente, } obj.write(vals) @api.multi def copie_cout_actualise_dans_cout_budget(self): for obj in self: self._copie_cout_actualise_dans_cout_budget(obj) @api.multi def initialisation_prix_vente_standard(self): for obj in self: obj.cout_std_prix_vente = obj.prix_vente-obj.amortissement_moule-obj.surcout_pre_serie @api.model def print_btn_report(self): threaded_calculation = threading.Thread(target=self.save_cout_report, args=()) threaded_calculation.start() return True def save_cout_report(self): user = self.env['res.users'].browse(self._uid) with api.Environment.manage(): new_cr = self.pool.cursor() self = self.with_env(self.env(cr=new_cr)) report_service = 'is_plastigray.report_is_cout' #file_param = self.env['ir.config_parameter'].get_param('path_report_pdf') db=self._cr.dbname path="/tmp/couts-" + db cde="rm -Rf " + path os.popen(cde).readlines() if not os.path.exists(path): os.makedirs(path) recs=self.search([], order="name",limit=50000) nb=len(recs) _logger.info("#### Début sauvegarde Coûts ####") ct=0 for rec in recs: ct=ct+1 code_pg=rec.name.is_code _logger.info('- '+str(ct)+'/'+str(nb)+' : '+str(code_pg)) result, format = self.env['report'].get_pdf(rec, report_service), 'pdf' file_name = path + '/'+str(code_pg) +'.pdf' fd = os.open(file_name,os.O_RDWR|os.O_CREAT) try: os.write(fd, result) finally: os.close(fd) filename="/var/www/odoo/couts/"+db+".zip" cde="rm -f " + filename + " && cd /tmp && zip -r " + filename + " couts-" +db+" && chmod 755 "+filename os.popen(cde).readlines() self.send_mail_notyfy_user() new_cr.close() _logger.info("#### Fin sauvegarde Coûts ####") return {} def send_mail_notyfy_user(self): db=self._cr.dbname user = self.env['res.users'].browse(self._uid) mail_pool = self.env['mail.mail'] values={} values.update({'subject': 'Génération des PDF des coûts terminée'}) values.update({'email_from': user.partner_id.email}) values.update({'email_to': user.partner_id.email}) values.update({'body_html': '<p>Bonjour,</p><p>Le zip contenant tous les PDF est disponible <a href="http://odoo/couts/'+db+'.zip">ici</a></p>' }) #values.update({'body': 'Bonjour,\nLe zip contenant tous les PDF est disonible ici http://odoo/couts-odoo1.zip' }) # values.update({'res_id': 'obj.id' }) #[optional] here is the record id, where you want to post that email after sending values.update({'model': 'is.cout' }) #[optional] here is the object(like 'project.project') to whose record id you want to post that email after sending msg_id = mail_pool.sudo().create(values) # And then call send function of the mail.mail, if msg_id: msg_id.send() return True @api.multi def cout_standard_indice_precedent(self): if len(self)>1: raise Warning(u"Modification multiple non autorisée !") for obj in self: is_code=obj.name.is_code indice=is_code[6:7] if indice=='': raise Warning(u"Code sans indice !") code=is_code[0:6] if indice!='A': code=code+chr(ord(indice)-1) couts=self.env['is.cout'].search([('name.is_code', '=', code)]) if len(couts)==0: raise Warning(u"Coût précédent non trouvé !") for cout in couts: obj.cout_std_matiere = cout.cout_std_matiere obj.cout_std_condition = cout.cout_std_condition obj.cout_std_machine = cout.cout_std_machine obj.cout_std_mo = cout.cout_std_mo obj.cout_std_st = cout.cout_std_st obj.cout_std_total = cout.cout_std_total obj.cout_std_prix_vente = cout.cout_std_prix_vente
class res_partner(models.Model): _inherit = 'res.partner' # F#13145 In the tree view of partner, # replace the column Phone by "Phone/Mobile" @api.multi def _get_full_phone(self): for record in self: full_phone = [] if record.phone: full_phone.append(record.phone) if record.mobile: full_phone.append(record.mobile) record.phone_mobile = " / ".join(full_phone) if full_phone else '' @api.depends('is_company') def _get_title_domain(self): """ Use for the domain on field Title: - NOT Is Company: titles with type `contact` - Is Company: titles with type `partner` """ for record in self: if record.is_company: record.title_domain = 'partner' else: record.title_domain = 'contact' phone_mobile = fields.Char(compute='_get_full_phone', string='Phone/Mobile') event_ids = fields.One2many(comodel_name='trobz.crm.event', inverse_name='partner_id', string='Events') lead_ids = fields.One2many(comodel_name='crm.lead', inverse_name='partner_id', string='Leads') skype_contact = fields.Char('Skype Contact', size=64) linkedin_profile = fields.Char('Linkedin Profile', size=64) create_uid = fields.Many2one(comodel_name='res.users', string='Creator') create_date = fields.Datetime('Creation Date') prospect = fields.Boolean( 'Prospect', help="Check this box if this contact is a prospect.") business_sector_id = fields.Many2one( comodel_name='trobz.crm.business.sector', string='Business Sector') title_domain = fields.Selection([('contact', 'Contact'), ('partner', 'Partner')], 'Title Domain', compute='_get_title_domain', store=True) @api.onchange('customer') def check_cutomer(self): if self.customer: self.prospect = False @api.onchange('prospect') def check_prospect(self): if self.prospect: self.customer = False @api.onchange('parent_id') def onchange_parent_id(self): if self.parent_id: self.customer = self.parent_id.customer self.prospect = self.parent_id.prospect self.supplier = self.parent_id.supplier # Can not migrate this function because onchange_state is called from Odoo def onchange_state(self, cr, uid, ids, state_id, context=None): if state_id: country_id = self.pool.get('res.country.state').browse( cr, uid, state_id, context).country_id.id return {'value': {'country_id': country_id}} return {} @api.model def default_get(self, fields): ctx = self._context and self._context.copy() or {} res = super(res_partner, self).default_get(fields) res.update({ 'is_company': False, }) # F#12640 : Update Related User and company for Partner.contact # Get customer/prospect/supplier like company if ctx.get('default_parent_id', False): parent_obj = self.browse(ctx['default_parent_id']) res.update({ 'customer': parent_obj.customer, 'prospect': parent_obj.prospect, 'supplier': parent_obj.supplier, 'use_parent_address': True, }) return res @api.multi def write(self, vals): context = self._context and self._context.copy() or {} context.update({'from_partner': 1}) res = super(res_partner, self).write(vals) # F#12640: For partner set as is_company, when updating the fields # ‘Customer/Supplier/Prospect’, automatically set the same value # on the contacts of that company. child_new_vals = {} if 'customer' in vals: child_new_vals['customer'] = vals.get('customer', False) if 'prospect' in vals: child_new_vals['prospect'] = vals.get('prospect', False) if 'supplier' in vals: child_new_vals['supplier'] = vals.get('supplier', False) if child_new_vals: for parent_obj in self: if parent_obj.is_company and parent_obj.child_ids: parent_obj.child_ids.write(child_new_vals) return res @api.multi def name_get(self): context = self._context or {} if not self: return [] res = [] for r in self: if context.get('contact_display', 'contact') == 'partner' and \ r.parent_id: res.append((r.id, r.parent_id.id)) else: res.append((r.id, r.name or '/')) return res @api.model def name_search(self, name='', args=None, operator='ilike', limit=100): if not args: args = [] context = dict(self._context) if 'crm_partner_id' in context: crm_partner_id = context.get('crm_partner_id', False) if not crm_partner_id: args = [('id', 'in', [])] else: partners = self.env['res.partner'].search([('id', '=', crm_partner_id)]) if partners: partner = partners[0] contact_ids = [contact.id for contact in partner.child_ids] args = [('id', 'in', contact_ids)] return super(res_partner, self).name_search(name=name, args=args, operator=operator, limit=limit)
class SaleOrderLine(models.Model): _name = 'sale.order.line' _description = 'Sales Order Line' _order = 'order_id desc, sequence, id' @api.depends('state', 'product_uom_qty', 'qty_delivered', 'qty_to_invoice', 'qty_invoiced') def _compute_invoice_status(self): """ Compute the invoice status of a SO line. Possible statuses: - no: if the SO is not in status 'sale' or 'done', we consider that there is nothing to invoice. This is also hte default value if the conditions of no other status is met. - to invoice: we refer to the quantity to invoice of the line. Refer to method `_get_to_invoice_qty()` for more information on how this quantity is calculated. - upselling: this is possible only for a product invoiced on ordered quantities for which we delivered more than expected. The could arise if, for example, a project took more time than expected but we decided not to invoice the extra cost to the client. This occurs onyl in state 'sale', so that when a SO is set to done, the upselling opportunity is removed from the list. - invoiced: the quantity invoiced is larger or equal to the quantity ordered. """ precision = self.env['decimal.precision'].precision_get( 'Product Unit of Measure') for line in self: if line.state not in ('sale', 'done'): line.invoice_status = 'no' elif not float_is_zero(line.qty_to_invoice, precision_digits=precision): line.invoice_status = 'to invoice' elif line.state == 'sale' and line.product_id.invoice_policy == 'order' and\ float_compare(line.qty_delivered, line.product_uom_qty, precision_digits=precision) == 1: line.invoice_status = 'upselling' elif float_compare(line.qty_invoiced, line.product_uom_qty, precision_digits=precision) >= 0: line.invoice_status = 'invoiced' else: line.invoice_status = 'no' @api.depends('product_uom_qty', 'discount', 'price_unit', 'tax_id') def _compute_amount(self): """ Compute the amounts of the SO line. """ for line in self: price = line.price_unit * (1 - (line.discount or 0.0) / 100.0) taxes = line.tax_id.compute_all(price, line.order_id.currency_id, line.product_uom_qty, product=line.product_id, partner=line.order_id.partner_id) line.update({ 'price_tax': taxes['total_included'] - taxes['total_excluded'], 'price_total': taxes['total_included'], 'price_subtotal': taxes['total_excluded'], }) @api.depends('product_id.invoice_policy', 'order_id.state') def _compute_qty_delivered_updateable(self): for line in self: line.qty_delivered_updateable = line.product_id.invoice_policy in ( 'order', 'delivery' ) and line.order_id.state == 'sale' and line.product_id.track_service == 'manual' @api.depends('qty_invoiced', 'qty_delivered', 'product_uom_qty', 'order_id.state') def _get_to_invoice_qty(self): """ Compute the quantity to invoice. If the invoice policy is order, the quantity to invoice is calculated from the ordered quantity. Otherwise, the quantity delivered is used. """ for line in self: if line.order_id.state in ['sale', 'done']: if line.product_id.invoice_policy == 'order': line.qty_to_invoice = line.product_uom_qty - line.qty_invoiced else: line.qty_to_invoice = line.qty_delivered - line.qty_invoiced else: line.qty_to_invoice = 0 @api.depends('invoice_lines.invoice_id.state', 'invoice_lines.quantity') def _get_invoice_qty(self): """ Compute the quantity invoiced. If case of a refund, the quantity invoiced is decreased. Note that this is the case only if the refund is generated from the SO and that is intentional: if a refund made would automatically decrease the invoiced quantity, then there is a risk of reinvoicing it automatically, which may not be wanted at all. That's why the refund has to be created from the SO """ for line in self: qty_invoiced = 0.0 for invoice_line in line.invoice_lines: if invoice_line.invoice_id.state != 'cancel': if invoice_line.invoice_id.type == 'out_invoice': qty_invoiced += invoice_line.quantity elif invoice_line.invoice_id.type == 'out_refund': qty_invoiced -= invoice_line.quantity line.qty_invoiced = qty_invoiced @api.depends('price_subtotal', 'product_uom_qty') def _get_price_reduce(self): for line in self: line.price_reduce = line.price_subtotal / line.product_uom_qty if line.product_uom_qty else 0.0 @api.multi def _compute_tax_id(self): for line in self: fpos = line.order_id.fiscal_position_id or line.order_id.partner_id.property_account_position_id if fpos: # The superuser is used by website_sale in order to create a sale order. We need to make # sure we only select the taxes related to the company of the partner. This should only # apply if the partner is linked to a company. if self.env.uid == SUPERUSER_ID and line.order_id.company_id: taxes = fpos.map_tax(line.product_id.taxes_id).filtered( lambda r: r.company_id == line.order_id.company_id) else: taxes = fpos.map_tax(line.product_id.taxes_id) line.tax_id = taxes else: line.tax_id = line.product_id.taxes_id if line.product_id.taxes_id else False @api.multi def _prepare_order_line_procurement(self, group_id=False): self.ensure_one() return { 'name': self.name, 'origin': self.order_id.name, 'date_planned': datetime.strptime(self.order_id.date_order, DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(days=self.customer_lead), 'product_id': self.product_id.id, 'product_qty': self.product_uom_qty, 'product_uom': self.product_uom.id, 'company_id': self.order_id.company_id.id, 'group_id': group_id, 'sale_line_id': self.id } @api.multi def _action_procurement_create(self): """ Create procurements based on quantity ordered. If the quantity is increased, new procurements are created. If the quantity is decreased, no automated action is taken. """ precision = self.env['decimal.precision'].precision_get( 'Product Unit of Measure') new_procs = self.env['procurement.order'] #Empty recordset for line in self: if line.state != 'sale' or not line.product_id._need_procurement(): continue qty = 0.0 for proc in line.procurement_ids: qty += proc.product_qty if float_compare(qty, line.product_uom_qty, precision_digits=precision) >= 0: return False if not line.order_id.procurement_group_id: vals = line.order_id._prepare_procurement_group() line.order_id.procurement_group_id = self.env[ "procurement.group"].create(vals) vals = line._prepare_order_line_procurement( group_id=line.order_id.procurement_group_id.id) vals['product_qty'] = line.product_uom_qty - qty new_proc = self.env["procurement.order"].create(vals) new_procs += new_proc new_procs.run() return new_procs @api.model def _get_analytic_invoice_policy(self): return ['cost'] @api.model def _get_analytic_track_service(self): return [] @api.model def create(self, values): line = super(SaleOrderLine, self).create(values) if line.state == 'sale': if line.product_id.track_service in self._get_analytic_track_service( ) or line.product_id.invoice_policy in self._get_analytic_invoice_policy( ) and not line.order_id.project_id: line.order_id._create_analytic_account() line._action_procurement_create() return line @api.multi def write(self, values): lines = False if 'product_uom_qty' in values: precision = self.env['decimal.precision'].precision_get( 'Product Unit of Measure') lines = self.filtered( lambda r: r.state == 'sale' and float_compare( r.product_uom_qty, values['product_uom_qty'], precision_digits=precision) == -1) result = super(SaleOrderLine, self).write(values) if lines: lines._action_procurement_create() return result order_id = fields.Many2one('sale.order', string='Order Reference', required=True, ondelete='cascade', index=True, copy=False) name = fields.Text(string='Description', required=True) sequence = fields.Integer(string='Sequence', default=10) invoice_lines = fields.Many2many('account.invoice.line', 'sale_order_line_invoice_rel', 'order_line_id', 'invoice_line_id', string='Invoice Lines', copy=False) invoice_status = fields.Selection([('upselling', 'Upselling Opportunity'), ('invoiced', 'Fully Invoiced'), ('to invoice', 'To Invoice'), ('no', 'Nothing to Invoice')], string='Invoice Status', compute='_compute_invoice_status', store=True, readonly=True, default='no') price_unit = fields.Float('Unit Price', required=True, digits=dp.get_precision('Product Price'), default=0.0) price_subtotal = fields.Monetary(compute='_compute_amount', string='Subtotal', readonly=True, store=True) price_tax = fields.Monetary(compute='_compute_amount', string='Taxes', readonly=True, store=True) price_total = fields.Monetary(compute='_compute_amount', string='Total', readonly=True, store=True) price_reduce = fields.Monetary(compute='_get_price_reduce', string='Price Reduce', readonly=True, store=True) tax_id = fields.Many2many('account.tax', string='Taxes') discount = fields.Float(string='Discount (%)', digits=dp.get_precision('Discount'), default=0.0) product_id = fields.Many2one('product.product', string='Product', domain=[('sale_ok', '=', True)], change_default=True, ondelete='restrict', required=True) product_uom_qty = fields.Float( string='Quantity', digits=dp.get_precision('Product Unit of Measure'), required=True, default=1.0) product_uom = fields.Many2one('product.uom', string='Unit of Measure', required=True) qty_delivered_updateable = fields.Boolean( compute='_compute_qty_delivered_updateable', string='Can Edit Delivered', readonly=True, default=True) qty_delivered = fields.Float( string='Delivered', copy=False, digits=dp.get_precision('Product Unit of Measure'), default=0.0) qty_to_invoice = fields.Float( compute='_get_to_invoice_qty', string='To Invoice', store=True, readonly=True, digits=dp.get_precision('Product Unit of Measure'), default=0.0) qty_invoiced = fields.Float( compute='_get_invoice_qty', string='Invoiced', store=True, readonly=True, digits=dp.get_precision('Product Unit of Measure'), default=0.0) salesman_id = fields.Many2one(related='order_id.user_id', store=True, string='Salesperson', readonly=True) currency_id = fields.Many2one(related='order_id.currency_id', store=True, string='Currency', readonly=True) company_id = fields.Many2one(related='order_id.company_id', string='Company', store=True, readonly=True) order_partner_id = fields.Many2one(related='order_id.partner_id', store=True, string='Customer') state = fields.Selection([ ('draft', 'Quotation'), ('sent', 'Quotation Sent'), ('sale', 'Sale Order'), ('done', 'Done'), ('cancel', 'Cancelled'), ], related='order_id.state', string='Order Status', readonly=True, copy=False, store=True, default='draft') customer_lead = fields.Float( 'Delivery Lead Time', required=True, default=0.0, help= "Number of days between the order confirmation and the shipping of the products to the customer", oldname="delay") procurement_ids = fields.One2many('procurement.order', 'sale_line_id', string='Procurements') @api.multi def _prepare_invoice_line(self, qty): """ Prepare the dict of values to create the new invoice line for a sales order line. :param qty: float quantity to invoice """ self.ensure_one() res = {} account = self.product_id.property_account_income_id or self.product_id.categ_id.property_account_income_categ_id if not account: raise UserError(_('Please define income account for this product: "%s" (id:%d) - or for its category: "%s".') % \ (self.product_id.name, self.product_id.id, self.product_id.categ_id.name)) fpos = self.order_id.fiscal_position_id or self.order_id.partner_id.property_account_position_id if fpos: account = fpos.map_account(account) res = { 'name': self.name, 'sequence': self.sequence, 'origin': self.order_id.name, 'account_id': account.id, 'price_unit': self.price_unit, 'quantity': qty, 'discount': self.discount, 'uom_id': self.product_uom.id, 'product_id': self.product_id.id or False, 'invoice_line_tax_ids': [(6, 0, self.tax_id.ids)], 'account_analytic_id': self.order_id.project_id.id, } return res @api.multi def invoice_line_create(self, invoice_id, qty): """ Create an invoice line. The quantity to invoice can be positive (invoice) or negative (refund). :param invoice_id: integer :param qty: float quantity to invoice """ precision = self.env['decimal.precision'].precision_get( 'Product Unit of Measure') for line in self: if not float_is_zero(qty, precision_digits=precision): vals = line._prepare_invoice_line(qty=qty) vals.update({ 'invoice_id': invoice_id, 'sale_line_ids': [(6, 0, [line.id])] }) self.env['account.invoice.line'].create(vals) @api.multi @api.onchange('product_id') def product_id_change(self): if not self.product_id: return {'domain': {'product_uom': []}} vals = {} domain = { 'product_uom': [('category_id', '=', self.product_id.uom_id.category_id.id)] } if not (self.product_uom and (self.product_id.uom_id.category_id.id == self.product_uom.category_id.id)): vals['product_uom'] = self.product_id.uom_id product = self.product_id.with_context( lang=self.order_id.partner_id.lang, partner=self.order_id.partner_id.id, quantity=self.product_uom_qty, date=self.order_id.date_order, pricelist=self.order_id.pricelist_id.id, uom=self.product_uom.id) name = product.name_get()[0][1] if product.description_sale: name += '\n' + product.description_sale vals['name'] = name self._compute_tax_id() if self.order_id.pricelist_id and self.order_id.partner_id: vals['price_unit'] = self.env[ 'account.tax']._fix_tax_included_price(product.price, product.taxes_id, self.tax_id) self.update(vals) return {'domain': domain} @api.onchange('product_uom', 'product_uom_qty') def product_uom_change(self): if not self.product_uom: self.price_unit = 0.0 return if self.order_id.pricelist_id and self.order_id.partner_id: product = self.product_id.with_context( lang=self.order_id.partner_id.lang, partner=self.order_id.partner_id.id, quantity=self.product_uom_qty, date_order=self.order_id.date_order, pricelist=self.order_id.pricelist_id.id, uom=self.product_uom.id) self.price_unit = self.env['account.tax']._fix_tax_included_price( product.price, product.taxes_id, self.tax_id) @api.multi def unlink(self): if self.filtered(lambda x: x.state in ('sale', 'done')): raise UserError( _('You can not remove a sale order line.\nDiscard changes and try setting the quantity to 0.' )) return super(SaleOrderLine, self).unlink() @api.multi def _get_delivered_qty(self): ''' Intended to be overridden in sale_stock and sale_mrp :return: the quantity delivered :rtype: float ''' return 0.0
class MedicalPrescriptionOrder(models.Model): _name = 'medical.prescription.order' _description = 'Medical Prescription Order' notes = fields.Text() name = fields.Char( required=True, default=lambda s: s._default_name() ) patient_id = fields.Many2one( string='Patient', comodel_name='medical.patient', required=True, help='Primary patient this is regarding', ) physician_id = fields.Many2one( string='Physician', comodel_name='medical.physician', required=True, help='Physician that issued prescription', ) partner_id = fields.Many2one( string='Pharmacy', comodel_name='medical.pharmacy', ) prescription_order_line_ids = fields.One2many( string='Prescription Order Lines', comodel_name='medical.prescription.order.line', inverse_name='prescription_order_id', ) is_pregnancy_warning = fields.Boolean( string='Pregnant', help='Check this if the primary patient on prescription is pregnant', ) is_verified = fields.Boolean( string='Verified', help='Check this if the prescription has been confirmed as valid', ) date_prescription = fields.Datetime( string='Prescription Date', default=lambda s: fields.Datetime.now(), ) active = fields.Boolean( compute='_compute_active', store=True, ) @api.model def _default_name(self): return self.env['ir.sequence'].next_by_code( 'medical.prescription.order', ) @api.multi @api.depends('prescription_order_line_ids', 'prescription_order_line_ids.active', ) def _compute_active(self): for rec_id in self: if not rec_id.prescription_order_line_ids: rec_id.active = True continue rec_id.active = any( rec_id.prescription_order_line_ids.mapped('active') )
class realizar_pago_wizard(models.TransientModel): _name = "realizar.pago.wizard" @api.model def default_get(self, fields_list): origen = self.env['encargo.encargo'].search([ ('id', 'in', self.env.context.get('active_ids', [])) ]) res = models.TransientModel.default_get(self, fields_list) ids = self.env.context.get('active_ids', []) pagos = self.env['pagos.encargo.line'].browse(ids) wizard_obj = self.env['realizar.pago.wizard.lines'] ds = [] debe = 0 for pago in origen.pagos_line: if pago.state == 'draft': d = wizard_obj.create({ 'wizard_id': self.id, 'fecha': pago.fecha, 'importe': pago.importe, 'pago_line_id': pago.id, }) ds.append(d.id) debe += pago.importe res['cliente_id'] = origen.cliente_id.id res['importe_pendiente'] = debe res['pagos_line'] = ds return res def pagar(self, cr, uid, ids, context=None): for e in self.browse(cr, uid, ids, context=None): wizard_line_obj = self.pool.get('realizar.pago.wizard.lines') trabajo_obj = self.pool.get('trabajo.trabajo') wizard_ids = wizard_line_obj.search( cr, uid, [('select', '=', True), ('wizard_id', '=', e.encargo_origen_id.id)]) if not wizard_ids: raise osv.except_osv( _('Error!'), _("Seleccione al menos un pago a realizar.")) if wizard_ids: pagos_line_obj = self.pool.get('pagos.encargo.line') for w in wizard_ids: wid = wizard_line_obj.browse(cr, uid, w, context=context) pagos_line_obj.write(cr, uid, wid.pago_line_id.id, {'state': 'done'}) #crear un trabajo # trabajo_obj = self.pool.get('trabajo.trabajo') # predef_ob = self.pool.get('predefinida.predefinida') # predef_id = predef_ob.search(cr, uid, [('name','=','Pago parcial trabajo por encargo')]) # total = wid.importe # cliente_id = e.encargo_origen_id.cliente_id.id # vals = { # 'costo': total, # 'cliente': cliente_id, # 'name': predef_id[0], # } # nid = trabajo_obj.create(cr, uid, vals) # #eliminar los transientModels creados # wizard_obj = self.pool.get('realizar.pago.wizard') # wizard_ids = wizard_obj.search(cr, uid, []) # for w in wizard_ids: # wizard = wizard_obj.browse(cr, uid, w) # wizard.unlink() return True encargo_origen_id = fields.Many2one( 'encargo.encargo', 'Encargo', help="Encargo al que pertenecen los pagos a realizar.") cliente_id = fields.Many2one('cliente.cliente', 'Cliente') fecha = fields.Date( 'Fecha', help= "Fecha en la que se realiza el pago. No necesariamente tiene que coincidir con las fechas indicadas en la linea de pagos pendientes. Al realizar el pago, se registrara un trabajo cuya fecha coincide con la especificada en este campo." ) importe_pendiente = fields.Float('Importe pendiente (MN)') pagos_line = fields.One2many('realizar.pago.wizard.lines', 'wizard_id', 'Pagos line') _defaults = { 'fecha': fields.Date.today, }
class settlement(models.Model): """Settlement model""" _name = "settlement" name = fields.Char(string="Settlement period", required=True, readonly=True) total = fields.Float(string="Total", readonly=True) date_from = fields.Date(string="From") date_to = fields.Date(string="To") settlement_agent_id = fields.One2many("settlement.agent", "settlement_id", string="Settlement agents", readonly=True) date = fields.Datetime(string="Created Date", required=True, default=fields.Datetime.now()) # TODO: Workflow is necessary to manage 'cancel' state/transition state = fields.Selection([("invoiced", "Invoiced"), ("settled", "Settled"), ("cancel", "Cancel")], string="State", readonly=True, default="settled") def action_invoice_create(self, cursor, user, ids, journal_id, product_id, context=None): agents_pool = self.pool.get('settlement.agent') res = {} for settlement in self.browse(cursor, user, ids, context=context): settlement_agent_ids = map(lambda x: x.id, settlement.settlement_agent_id) invoices_agent = agents_pool.action_invoice_create( cursor, user, settlement_agent_ids, journal_id, product_id, context=context) res[settlement.id] = invoices_agent.values() return res def calculate(self, cr, uid, ids, agent_ids, date_from, date_to, context=None): """generate one settlement line per agent""" # Search for all settlements in a period sale_agent_pool = self.pool['sale.agent'] settlement_agent_pool = self.pool['settlement.agent'] agents = sale_agent_pool.browse(cr, uid, agent_ids, context=context) total = 0 for agent in agents: # generate one settlement line per agent liq_agent_id = settlement_agent_pool.create( cr, uid, { 'agent_id': agent.id, 'settlement_id': ids }, context=context) settlement_agent_pool.calculate(cr, uid, liq_agent_id, date_from, date_to, context=context) liq_agent = settlement_agent_pool.browse(cr, uid, liq_agent_id, context=context) total = total + liq_agent.total return self.write(cr, uid, ids, {'total': total}, context=context) def action_cancel(self, cr, uid, ids, context=None): """Cancel the liquidation""" agent_pool = self.pool['invoice.line.agent'] for settle in self.browse(cr, uid, ids, context=context): for settle_line in settle.settlement_agent_id: for line in settle_line.lines: line_id = line.invoice_line_id commissions = [x.id for x in line_id.commission_ids] commission_ids = line.invoice_line_id and commissions or [] if commission_ids: agent_pool.write(cr, uid, commission_ids, { 'settled': False, 'quantity': 0.0 }, context=context) return self.write(cr, uid, ids, {'state': 'cancel'}, context=context) def unlink(self, cr, uid, ids, context=None): """Allow to delete cancelled settlements""" for settle in self.browse(cr, uid, ids, context=context): if settle.state != 'cancel': raise exceptions.Warning( _("You can't delete it, if it isn't in cancel state.")) return super(settlement, self).unlink(cr, uid, ids, context=context)
class EnrollmentResponse(models.Model): _name = "hc.res.enrollment.response" _description = "Enrollment Response" identifier_ids = fields.One2many( comodel_name="hc.enrollment.response.identifier", inverse_name="enrollment_response_id", string="Identifiers", help="Business Identifier.") status_id = fields.Many2one(comodel_name="hc.vs.fm.status", string="Status", help="The status of the resource instance.") status_history_ids = fields.One2many( comodel_name="hc.enrollment.response.status.history", inverse_name="enrollment_response_id", string="Status History", help="The status of the enrollment response over time.") request_id = fields.Many2one(comodel_name="hc.res.enrollment.request", string="Request", help="Claim reference.") outcome = fields.Selection(string="Enrollment Response Outcome", selection=[("complete", "Complete"), ("error", "Error")], help="Transaction status: error, complete.") disposition = fields.Text(string="Disposition", help="Disposition Message.") ruleset_id = fields.Many2one(comodel_name="hc.vs.ruleset", string="Ruleset", help="Resource version.") original_ruleset_id = fields.Many2one(comodel_name="hc.vs.ruleset", string="Original Ruleset", help="Original version.") created = fields.Datetime(string="Creation Date", help="Creation date.") organization_id = fields.Many2one(comodel_name="hc.res.organization", string="Organization", help="Insurer.") request_provider_id = fields.Many2one(comodel_name="hc.res.practitioner", string="Request Provider", help="Responsible practitioner.") request_organization_id = fields.Many2one( comodel_name="hc.res.organization", string="Request Organization", help="Responsible organization.") @api.model def create(self, vals): status_history_obj = self.env['hc.enrollment.response.status.history'] res = super(EnrollmentResponse, self).create(vals) if vals and vals.get('status_id'): status_history_vals = { 'enrollment_response_id': res.id, 'status': res.status_id.name, 'start_date': datetime.today() } if vals.get('status_id') == 'entered-in-error': status_history_vals.update({'end_date': datetime.today()}) status_history_obj.create(status_history_vals) return res @api.multi def write(self, vals): status_history_obj = self.env['hc.enrollment.response.status.history'] fm_status_obj = self.env['hc.vs.fm.status'] res = super(EnrollmentResponse, self).write(vals) status_history_record_ids = status_history_obj.search([('end_date', '=', False)]) if status_history_record_ids: if vals.get('status_id') and status_history_record_ids[ 0].status != vals.get('status_id'): for status_history in status_history_record_ids: status_history.end_date = datetime.strftime( datetime.today(), DTF) time_diff = datetime.today() - datetime.strptime( status_history.start_date, DTF) if time_diff: days = str(time_diff).split(',') if days and len(days) > 1: status_history.time_diff_day = str(days[0]) times = str(days[1]).split(':') if times and times > 1: status_history.time_diff_hour = str(times[0]) status_history.time_diff_min = str(times[1]) status_history.time_diff_sec = str(times[2]) else: times = str(time_diff).split(':') if times and times > 1: status_history.time_diff_hour = str(times[0]) status_history.time_diff_min = str(times[1]) status_history.time_diff_sec = str(times[2]) fm_status = fm_status_obj.browse(vals.get('status_id')) status_history_vals = { 'enrollment_response_id': self.id, 'status': fm_status.name, 'start_date': datetime.today() } if vals.get('status_id') == 'entered-in-error': status_id_history_vals.update( {'end_date': datetime.today()}) status_history_obj.create(status_history_vals) return res
class ShiftTicket(models.Model): _inherit = 'event.event.ticket' _name = 'shift.ticket' _description = 'Shift Ticket' SHIFT_TYPE_SELECTION = [ ('standard', 'Standard'), ('ftop', 'FTOP'), ] shift_type = fields.Selection(selection=SHIFT_TYPE_SELECTION, string='Shift type', compute='compute_shift_type', store=True) name = fields.Char(translate=False) shift_id = fields.Many2one('shift.shift', "Shift", required=True, ondelete='cascade') event_id = fields.Many2one(required=False) product_id = fields.Many2one( default=lambda self: self._default_product_id(), domain=[("shift_type_id", "!=", False)], ) registration_ids = fields.One2many('shift.registration', 'shift_ticket_id', 'Registrations') date_begin = fields.Datetime(related="shift_id.date_begin") begin_date_string = fields.Char( string='Begin Date', compute='_compute_begin_date_fields', store=True, ) user_id = fields.Many2one('res.partner', related="shift_id.user_id", store=True) @api.multi @api.depends('date_begin') def _compute_begin_date_fields(self): for ticket in self: ticket.begin_date_string = ticket.date_begin and datetime.strftime( datetime.strptime(ticket.date_begin, "%Y-%m-%d %H:%M:%S") + timedelta(hours=2), "%d/%m/%Y %H:%M:%S") or False @api.model def _default_product_id(self): try: product = self.env['ir.model.data'].get_object( 'coop_shift', 'product_product_event') return product.id except ValueError: return False seats_availability = fields.Selection(compute='_compute_seats') seats_reserved = fields.Integer(compute='_compute_seats') seats_available = fields.Integer(compute='_compute_seats') seats_unconfirmed = fields.Integer(compute='_compute_seats') seats_used = fields.Integer(compute='_compute_seats', ) @api.depends('product_id') @api.multi def compute_shift_type(self): for ticket in self: if ticket.product_id.id ==\ self.env.ref("coop_shift.product_product_shift_ftop").id: ticket.shift_type = 'ftop' else: ticket.shift_type = 'standard' @api.multi @api.depends('seats_max', 'registration_ids.state') def _compute_seats(self): """ Determine reserved, available, reserved but unconfirmed and used seats. """ # initialize fields to 0 + compute seats availability for ticket in self: ticket.seats_availability = 'unlimited' if ticket.seats_max == 0\ else 'limited' ticket.seats_unconfirmed = ticket.seats_reserved =\ ticket.seats_used = ticket.seats_available = 0 # aggregate registrations by ticket and by state if self.ids: state_field = { 'draft': 'seats_unconfirmed', 'open': 'seats_reserved', 'done': 'seats_used', } query = """ SELECT shift_ticket_id, state, count(shift_id) FROM shift_registration WHERE shift_ticket_id IN %s AND state IN ('draft', 'open', 'done') GROUP BY shift_ticket_id, state """ self._cr.execute(query, (tuple(self.ids), )) for shift_ticket_id, state, num in self._cr.fetchall(): ticket = self.browse(shift_ticket_id) ticket[state_field[state]] += num # compute seats_available for ticket in self: if ticket.seats_max > 0: ticket.seats_available = ticket.seats_max - ( ticket.seats_reserved + ticket.seats_used) @api.multi @api.constrains('registration_ids', 'seats_max') def _check_seats_limit(self): for ticket in self: if ticket.seats_max and ticket.seats_available < 0: raise UserError(_('No more available seats for the ticket')) @api.onchange('product_id') def onchange_product_id(self): price = self.product_id.list_price if self.product_id else 0 return {'value': {'price': price}}
class HrLoan(models.Model): _name = 'hr.loan' @api.one @api.constrains('requested_amount') def _requested_so(self): # if self.requested_amount and self.remaining_budget: # if self.requested_amount < 0: # raise UserError(_('The Requested Amount Must Be Larger Than Zero.')) # if self.requested_amount > self.remaining_budget: # raise UserError(_('The Requested Amount Must Be Larger Or Equal Than Remaining Budget.')) if not self.requested_amount: raise UserError(_('The Requested Amount Is Required.')) # elif not self.remaining_budget: # raise UserError(_('The Remaining Budget IS Required.')) return True @api.multi def unlink(self): if self.state != 'draft': raise ValidationError( _('Error ! You Cannot Delete The Loan Tn This State ')) else: for line in self.loan_line: if line.state == 'unpaid': line.unlink() else: raise ValidationError( _('Error ! You Have Line Piad You Cannot Delete The Loan .' )) return super(HrLoan, self).unlink() # @api.one # @api.depends('employee_id') # def compute_remaining_budget(self): # remaining_budget = 0 # loan_line_obj = self.env['hr.loan.line'] # if self.employee_id: # # total_unpaid_amount = 0 # loan_line_data = loan_line_obj.search( # [('loan_id.employee_id', '=', self.employee_id.id), ('state', 'in', ('unpaid', 'partial_paid'))]) # for loan_line in loan_line_data: # total_unpaid_amount += loan_line.amount - loan_line.paid_amount # # contract_id = False # if self.employee_id.contract_id: # contract_id = self.employee_id.contract_id # # if contract_id: # total_loan_budget = contract_id.total_loan_budget # if total_unpaid_amount <= total_loan_budget: # remaining_budget = total_loan_budget - total_unpaid_amount # else: # remaining_budget = 0 # # self.remaining_budget = remaining_budget # return True name = fields.Char(required=True) # remaining_budget = fields.Float(string="Total Loan Budget", compute=compute_remaining_budget, store=True, # required=False, ) employee_id = fields.Many2one('hr.employee', string='Employee', required=True) company_id = fields.Many2one(related='employee_id.company_id', string='Company', readonly=True, store=True) requested_date = fields.Date(default=fields.Date.today()) settlement_date = fields.Date(string='Start Date For Settlement', default=fields.Date.today()) requested_amount = fields.Float(required=True) installment_amount = fields.Float(required=True) # installment = fields.Integer(string='Installment (months)', default=1, required=True) state = fields.Selection([('draft', 'Draft'), ('cancel', 'Cancelled'), ('approved', 'Approved'), ('sent', 'Sent'), ('closed', 'Closed')], default='draft', readonly=True, copy=False, track_visibility='onchange') loan_line = fields.One2many('hr.loan.line', 'loan_id') journal_created = fields.Boolean(default=False) @api.one def action_approved(self): """ Put the state of the Loan into approved state """ if self.loan_line: requested_amount = self.requested_amount total_amount = sum((one_line.amount - one_line.paid_amount) for one_line in self.loan_line) if total_amount != requested_amount: raise ValidationError( _('Error ! Total Amount In lines Must Be Equal The Requested Amount.' )) self.write({'state': 'approved'}) else: raise ValidationError( _('Error ! You Cannot Approved Without Lines .')) return True @api.one def action_cancel(self): """ Put the state of the Loan into cancel state """ if self.state == 'draft': self.write({'state': 'cancel'}) else: raise ValidationError( _('Error ! You Cannot Cancel Loan In Approved State .')) return True @api.multi @api.depends('settlement_date', 'requested_amount', 'installment_amount') def action_populate(self): if self.installment_amount: loan_line = self.env['hr.loan.line'] if self.loan_line: for line in self.loan_line: line.unlink() # if len(self.loan_line) > 0: # raise UserError(_('Not allowed to populate more than one time')) requested_amount = self.requested_amount amount = self.installment_amount installment = int(math.floor(self.requested_amount / amount)) tagme3a = round(amount, 2) * float(installment) diff_nesba = 0 if requested_amount != tagme3a: diff_nesba = requested_amount - tagme3a if diff_nesba < 0: raise ValidationError( _('Total of installments is greater than the requested amount' )) date_start_dt = fields.Datetime.from_string(self.settlement_date) for i in range(installment): # add_diff = 0.0 # if i == 0: # add_diff = diff_nesba loan_line.create({ 'amount': round(amount, 2), 'date': date_start_dt.date(), 'loan_id': self.id, 'state': 'unpaid' }) date_start_dt = (date_start_dt + relativedelta(months=+1)) if diff_nesba > 0: loan_line.create({ 'amount': diff_nesba, 'date': date_start_dt.date(), 'loan_id': self.id, 'state': 'unpaid' }) else: raise ValidationError( _('Error ! Number Of Months Must Be Larger Than Zero .')) return True @api.multi def send_payment_request(self): if self.employee_id.address_home_id: if self.employee_id.address_home_id.customer == True: request_date = fields.Date.today() vals = { # 'name': self.env['ir.sequence'].next_by_code('loan.payment'), 'desc': self.name, 'req_amount': self.requested_amount, 'req_date': request_date, 'employee_id': self.employee_id.id, 'partner_id': self.employee_id.address_home_id.id, 'loan_line_ids': [(0, 0, { 'date': l.date, 'amount': l.amount, }) for l in self.loan_line] } loan_pay_obj = self.env['loan.payment'].create(vals) loan_pay_obj.partner_type = 'customer' self.state = 'sent' return { 'name': 'Message', 'type': 'ir.actions.act_window', 'view_type': 'form', 'view_mode': 'form', 'res_model': 'acc.loan.payment.view.wizard', 'target': 'new', 'context': { 'default_name': "Payment Loan Request Sent Successfully." } } else: raise ValidationError( 'Related Partner of Employee is not a Customer') else: raise ValidationError('Employee does not has Related Partner')
class AccountCheckbook(models.Model): _name = 'account.checkbook' _inherit = ['mail.thread'] @api.depends('account_own_check_ids') def _get_checks(self): for checkbook in self: checkbook.account_draft_own_check_ids = \ checkbook.account_own_check_ids.filtered(lambda x: x.state == 'draft') checkbook.account_used_own_check_ids = \ checkbook.account_own_check_ids.filtered(lambda x: x.state != 'draft') @api.multi def _search_draft_checks(self, operator, value): recs = self.env['account.own.check'].search([('state', '=', 'draft')]) return [('id', 'in', recs.ids)] name = fields.Char('Referencia', required=True, track_visibility='onchange') account_id = fields.Many2one( 'account.account', 'Cuenta', help= "Seleccionar una cuenta contable si no se desea utilizar la de la cuenta bancaria", track_visibility='onchange') payment_type = fields.Selection([('common', 'Comun'), ('postdated', 'Diferido')], string="Tipo", required=True, default='common', track_visibility='onchange') journal_id = fields.Many2one('account.journal', 'Cuenta bancaria', domain=[('type', '=', 'bank')], required=True, track_visibility='onchange') account_own_check_ids = fields.One2many('account.own.check', 'checkbook_id', 'Cheques') account_draft_own_check_ids = fields.One2many('account.own.check', 'checkbook_id', 'Cheques', compute=_get_checks, search=_search_draft_checks) account_used_own_check_ids = fields.One2many('account.own.check', 'checkbook_id', 'Cheques', compute=_get_checks) number_from = fields.Char('Desde', required=True, track_visibility='onchange') number_to = fields.Char('Hasta', required=True, track_visibility='onchange') active = fields.Boolean(string='Activo', default=True, track_visibility='onchange') currency_id = fields.Many2one('res.currency', string='Moneda', related='journal_id.currency_id', readonly=True) @api.constrains('number_from', 'number_to') def constraint_numbers(self, max_checks=100): for checkbook in self: if not (checkbook.number_from.isdigit() or checkbook.number_to.isdigit()): raise ValidationError( "La numeracion debe contener solo numeros") if int(checkbook.number_from) <= 0 or int( checkbook.number_to) <= 0: raise ValidationError("La numeracion debe ser mayor a cero.") if int(checkbook.number_from) > int(checkbook.number_to): raise ValidationError( "Numeracion Desde no puede ser mayor a Hasta.") if int(checkbook.number_to) - int( checkbook.number_from) + 1 > max_checks: raise ValidationError( "El rango de cheques de la chequera es muy grande.\n" "No puede superar los {} cheques".format(max_checks)) def generate_checks(self): """ Crea todos los cheques faltantes de cada chequera seleccionada """ for checkbook in self: if self.account_used_own_check_ids: raise ValidationError( "Solo se pueden generar cheques en una chequera no utilizada." ) total_numbers = range(int(checkbook.number_from), int(checkbook.number_to) + 1) checkbook._validate_checkbook_fields() bank_id = checkbook.journal_id.bank_id.id # Borramos los cheques viejos en caso que ninguno se haya usado, por si cambian el rango if not checkbook.account_used_own_check_ids: checkbook.account_own_check_ids.unlink() # El nombre del cheque puede contener solo digitos actual_numbers = [ int(number) for number in checkbook.account_own_check_ids.mapped('name') ] # Creamos cheques segun la numeracion de la chequera para los cheques no existentes for check_number in set(total_numbers) - set(actual_numbers): self.account_own_check_ids.create({ 'checkbook_id': checkbook.id, 'name': str(check_number), 'bank_id': bank_id, 'payment_type': checkbook.payment_type, 'currency_id': checkbook.currency_id.id or self.env.user.company_id.currency_id.id, }) def _validate_checkbook_fields(self): if not self.journal_id.bank_id: raise ValidationError( "Falta configurar el banco en la cuenta bancaria de la chequera" )
class DocumentPage(models.Model): """This class is use to manage Document.""" _name = "document.page" _inherit = ['mail.thread'] _description = "Document Page" _order = 'name' name = fields.Char('Title', required=True) type = fields.Selection([('content', 'Content'), ('category', 'Category')], 'Type', help="Page type", default="content") parent_id = fields.Many2one('document.page', 'Category', domain=[('type', '=', 'category')]) child_ids = fields.One2many('document.page', 'parent_id', 'Children') content = fields.Text("Content") display_content = fields.Text(string='Displayed Content', compute='_get_display_content') history_ids = fields.One2many('document.page.history', 'page_id', 'History') menu_id = fields.Many2one('ir.ui.menu', "Menu", readonly=True) create_date = fields.Datetime("Created on", select=True, readonly=True) create_uid = fields.Many2one('res.users', 'Author', select=True, readonly=True) write_date = fields.Datetime("Modification Date", select=True, readonly=True) write_uid = fields.Many2one('res.users', "Last Contributor", select=True, readonly=True) def _get_page_index(self, page, link=True): """Return the index of a document.""" index = [] for subpage in page.child_ids: index += ["<li>" + self._get_page_index(subpage) + "</li>"] r = '' if link: r = '<a href="#id=%s">%s</a>' % (page.id, page.name) if index: r += "<ul>" + "".join(index) + "</ul>" return r def _get_display_content(self): """Return the content of a document.""" for page in self: if page.type == "category": display_content = self._get_page_index(page, link=False) else: display_content = page.content page.display_content = display_content @api.onchange("parent_id") def do_set_content(self): """We Set it the right content to the new parent.""" if self.parent_id and not self.content: if self.parent_id.type == "category": self.content = self.parent_id.content def create_history(self, page_id, content): """Create the first history of a newly created document.""" history = self.env['document.page.history'] return history.create({"content": content, "page_id": page_id}) @api.multi def write(self, vals): """Write the content and set the history.""" result = super(DocumentPage, self).write(vals) content = vals.get('content') if content: for page in self: self.create_history(page.id, content) return result @api.model @api.returns('self', lambda value: value.id) def create(self, vals): """Create the first history of a document.""" page_id = super(DocumentPage, self).create(vals) content = vals.get('content') if content: self.create_history(page_id.id, content) return page_id
class sd_excel_toimport (models.Model): _name = "sd.excel.toimport" _inherit = ['mail.thread', 'ir.needaction_mixin'] name = fields.Char (string = "Description", required = True) state = fields.Selection (string = "State", selection = [('0', 'Draft'), ('1', 'In progress'), ('2', 'Imported/Done')], default = '0', required = True, readonly = True) model_id = fields.Many2one('ir.model', required='True', string='Select Model', help = "Type of model odoo to import") fields_ids = fields.Many2many ('ir.model.fields', 'ir_model_fields_sd_excel_toimport_fields', string = "To_read", depends = 'model_id', domain="[('model_id','=',model_id)]", required = True, help = "Columns from read of excel document, the order must match") fields_in_ids = fields.Many2many ('ir.model.fields', 'ir_model_fields_sd_excel_toimport_fields_in', string = "To import", domain="[('id','in',fields_ids[0][2]),('id','!=',field_id)]", help = "Columns from import of excel document") field_id = fields.Many2one ('ir.model.fields', depends = 'fields_ids', string = "Link to import", domain="[('id','in',fields_ids[0][2]),('name','in',['default_code','ref','vat','name','seller_code','ean13','email'])]", help = "Field to link between Excel and Odoo Model") file = fields.Binary (string = "Excel document (XLS)", help = "Document in .xls format", default = None, copy = False) sd_excel_line_id = fields.One2many ('sd.excel.line', 'sd_excel_toimport_id', string = "Lines to import", readonly = True) view_ref_id = fields.Many2one ('ir.ui.view', string = "view_ref", default = None) not_found = fields.Boolean (string = "Import product not found", help = "if the field is true, the not found products are created, otherwise only overwrite existing", default = False) auto_create = fields.Boolean (string = "Auto create", help = "If you activate this selection, the products that not created are created automaticly", default = False) customer = fields.Boolean (string = 'Is customer', default = False) supplier = fields.Boolean (string = 'Is supplier', default = False) @api.model def create (self, vals): res = super (sd_excel_toimport, self).create (vals) res.message_post (body = _("Excel import %s with name %s created" % (res.id, res.name))) return res @api.multi def copy (self, default): default['name'] = _("%s (copy)") % self.name default['view_ref_id'] = None return super(sd_excel_toimport, self).copy (default) @api.multi def unlink (self): res = [] for i in self: if i.state == '2': raise exceptions.Warning (_("You can't delete a import done")) elif i.state == '1': i.pool.get ('ir.ui.view').browse (i._cr, i._uid, [i.view_ref_id.id]).sudo ().unlink () i.sd_excel_line_id.sudo ().unlink () res.append (super (sd_excel_toimport, i).unlink ()) return res @api.multi def button_read (self): in_progress = super (sd_excel_toimport, self).search ([('state','=','1')]) if len (in_progress) > 0: msg = '' for i in in_progress: msg += str (i.name) + ' ' raise exceptions.Warning (_("You must complete or delete the import %s\nThere can only be an import in progress" % msg)) if len (self.field_id) == 1 and self.file != None and len (self.fields_in_ids) != 0: if self.model_id.model == "product.template": return self.read_excel () elif self.model_id.model == "res.partner": return self.read_excel (True) elif len (self.field_id) == 0: raise exceptions.Warning (_("Error on Document Type\nPlease, Select link to import")) elif len (self.fields_in_ids) == 0: raise exceptions.Warning (_("Error on Document Type\nPlease, Insert fields to import")) elif self.file == None: raise exceptions.Warning (_("Error on Document Type\nPlease, Insert Excel document")) else: raise exceptions.Warning (_("Error on Document Type")) @api.multi def button_import (self): res = self.write_data () if res == True: self.message_post (body = _("Document %s imported" % self.name)) self.state = "2" self.pool.get ('ir.ui.view').browse (self._cr, self._uid, [self.view_ref_id.id]).sudo ().unlink () return res @api.multi def button_back (self): try: if self.state == '1': self.pool.get ('ir.ui.view').browse (self._cr, self._uid, [self.view_ref_id.id]).sudo ().unlink () self.sd_excel_line_id.unlink () self.message_post (body = _("Document %s change state -> Draft" % self.name)) self.state = "0" except: raise exceptions.Warning (_("Error on delete")) @api.multi def read_excel (self, partner = False): try: file_path = tempfile.gettempdir () + '/' + str (self._cr.dbname) + str (self._uid) + '.xls' f = open (file_path, 'wb') f.write (self.file.decode ('base64')) f.close () wb = xlrd.open_workbook (file_path) ws = wb.sheet_by_index (0) except: raise exceptions.Warning (_("Error on read Excel")) if len (self.fields_ids) == ws.ncols and ws.ncols <= 10: try: to_imp = [] for i in self.fields_in_ids: to_imp.append (i.id) for r in range (1, ws.nrows): a = self.sd_excel_line_id.create ({'name': str (r), 'sd_excel_toimport_id': self.id}) aux = '' for c in range (ws.ncols): if self.field_id.name == self.fields_ids[c].name: aux = self.env[str (self.model_id.model)].search ([(self.field_id.name, '=', ws.cell (r,c).value)]) if len (aux) > 1: raise exceptions.Warning (_ ('%s %s in row %s has multiple matches' % (ws.cell (r,c).value, self.field_id.name, r))) elif len (aux) <= 0: aux = '' break for c in range(ws.ncols): a.write ({'key%d' % c: ws.cell (r,c).value,}) if self.fields_ids[c].id in to_imp: if aux != '': a.write ({'old_key%d' % c: str (aux[self.fields_ids[c].name])}) a.write ({'ralation_model_id': aux.id}) else: a.write ({'old_key%d' % c: _('Product not found')}) self.message_post (body = _("Read Excel document %s" % self.name)) return self.view_create (to_imp) except: raise exceptions.Warning (_("Error on write excel in sd_excel_line table")) else: raise exceptions.Warning (_("The columns of excel and number of fields don't match")) def view_create (self, to_imp): try: arch = """<?xml version="1.0"?> <tree string="Excel Import">""" for c in range (len (self.fields_ids)): arch = arch + "\n<field name='key%d' string='%s'/>" % (c, self.fields_ids[c].field_description) if self.fields_ids[c].id in to_imp: arch = arch + "\n<field name='old_key%d' string='%s'/>" % (c, _('old ') + self.fields_ids[c].field_description) arch = arch + "\n</tree>" self.view_ref_id = self.env['ir.ui.view'].sudo().create ({'name': 'view.excel.line.tree', 'arch': arch, 'priority': 16, 'mode': 'primary', 'active': 1, 'model': 'sd.excel.line', 'type': 'tree'}) self.state = "1" return { 'type': 'ir.actions.act_window', 'res_model': 'sd.excel.toimport', 'view_type': 'form', 'view_mode': 'form', 'auto_refresh':1, 'res_id': self.id, } except: raise exceptions.Warning (_("Error on view_create")) @api.multi def write_data (self): to_imp = [] for i in self.fields_in_ids: to_imp.append (i.id) for line in self.sd_excel_line_id: if line.ralation_model_id: try: key_count = 0 model_to_in = self.pool.get (str (self.model_id.model)).browse (self._cr, self._uid, [line.ralation_model_id]) for fiel in self.fields_ids: if fiel.id in to_imp: clave = 'key%d' % key_count model_to_in.write ({str (fiel.name): line[clave]}) key_count += 1 except: raise exceptions.Warning (_('Error to write in %s %s' % (self.model_id.model, model_to_in.name))) elif self.not_found: try: if self.auto_create: key_count = 0 model_to_create = self.env[str (self.model_id.model)].create ({'name': 'temp'}) line.ralation_model_id = model_to_create.id for fiel in self.fields_ids: clave = 'key%d' % key_count model_to_create.write ({fiel.name: line[clave]}) key_count += 1 self.message_post (body = _("Created product %s" % model_to_create.name)) else: key_count = 0 model_to_create = self.env[str (self.model_id.model)].create ({'name': 'temp'}) line.ralation_model_id = model_to_create.id for fiel in self.fields_ids: clave = 'key%d' % key_count model_to_create.write ({fiel.name: line[clave]}) key_count += 1 self.message_post (body = _("Created product %s" % model_to_create.name)) return {'name': str (self.model_id.name), 'type': 'ir.actions.act_window', 'res_model': str (self.model_id.model), 'view_type': 'form', 'view_mode': 'form', 'target': 'new', 'auto_refresh':1, 'res_id': model_to_create.id, 'flags': {'action_buttons': True}, } except: raise exceptions.Warning (_('Error to create in %s %s' % (self.model_id.model, model_to_in.name))) return True
class res_partner(models.Model): _inherit = "res.partner" attachment_ids = fields.One2many('ir.attachment', 'partner_id', 'Attachments')
class dym_production_stock_lot(models.Model): _inherit = 'stock.production.lot' @api.multi def name_onchange(self, product_id, name): dom = {} # product_ids = self.env['product.category'].get_child_ids('Unit') # dom['product_id']=[('categ_id','in',product_ids)] product_ids = [] if name: kd_mesin = name[:5] name = name.replace(' ', '').upper() kd_mesin = kd_mesin.replace(' ', '').upper() prod_tmpl_src = self.env["product.template"].search([ ("kd_mesin", "=", kd_mesin) ]) if prod_tmpl_src: for x in prod_tmpl_src: prod_prod_src = self.env["product.product"].search([ ("product_tmpl_id", "=", x.id) ]) if prod_prod_src: for y in prod_prod_src: product_ids.append(y.id) dom['product_id'] = [('id', 'in', product_ids)] return {'value': {'name': name, 'state': 'workshop'}, 'domain': dom} @api.multi def chassis_onchange(self, chassis_no): if chassis_no: chassis_no = chassis_no.replace(' ', '').upper() return {'value': {'chassis_no': chassis_no}} @api.multi def nosin_onchange(self, name): dom = {} product_ids = [] if name: kd_mesin = name[:5] name = name.replace(' ', '').upper() kd_mesin = kd_mesin.replace(' ', '').upper() prod_tmpl_src = self.env["product.template"].search([ ("kd_mesin", "=", kd_mesin) ]) if prod_tmpl_src: for x in prod_tmpl_src: prod_prod_src = self.env["product.product"].search([ ("product_tmpl_id", "=", x.id) ]) if prod_prod_src: for y in prod_prod_src: product_ids.append(y.id) dom['product_id'] = [('id', 'in', product_ids)] return {'domain': dom} @api.multi def no_pol_onchange(self, no_polisi): warning = {} value = {} result = {} if no_polisi: formatted_no_polisi = '' no_polisi_normalize = no_polisi.replace(' ', '').upper() splitted_no_polisi = re.findall(r'[A-Za-z]+|\d+', no_polisi_normalize) if len(splitted_no_polisi) == 3: if splitted_no_polisi[0].isalpha( ) == True and splitted_no_polisi[1].isdigit( ) == True and splitted_no_polisi[2].isalpha() == True: for word in splitted_no_polisi: formatted_no_polisi += word + ' ' formatted_no_polisi = formatted_no_polisi[:-1] return {'value': {'no_polisi': formatted_no_polisi}} warning = { 'title': ('Perhatian !'), 'message': (('Format nomor polisi salah, mohon isi nomor polisi dengan format yang benar! (ex. A 1234 BB)' )), } value['no_polisi'] = self.no_polisi result['warning'] = warning result['value'] = value return result @api.multi def kode_buku_onchange(self, kode_buku): if kode_buku: kode_buku = kode_buku.replace(' ', '').upper() return {'value': {'kode_buku': kode_buku}} @api.multi def nama_buku_onchange(self, nama_buku): if nama_buku: nama_buku = nama_buku.replace(' ', '').upper() return {'value': {'nama_buku': nama_buku}} work_order_ids = fields.One2many('dym.work.order', 'lot_id', string="Work Orders", readonly=True) def name_get(self, cr, uid, ids, context=None): if context is None: context = {} if isinstance(ids, (int, long)): ids = [ids] res = [] for record in self.browse(cr, uid, ids, context=context): name = record.name if record.no_polisi: name = "%s - %s" % (record.no_polisi, name) res.append((record.id, name)) return res @api.model def name_search(self, name, args=None, operator='ilike', limit=100): args = args or [] if name and len(name) >= 3: args = [ '|', ('name', operator, name), ('no_polisi', operator, name) ] + args categories = self.search(args, limit=limit) return categories.name_get() @api.model def create(self, vals): if not vals.get('product_id'): return False res = super(dym_production_stock_lot, self).create(vals) return res
class AccountAssetAsset(models.Model): _name = 'account.asset.asset' _description = 'Asset/Revenue Recognition' _inherit = ['mail.thread', 'ir.needaction_mixin'] account_move_ids = fields.One2many('account.move', 'asset_id', string='Entries', readonly=True, states={'draft': [('readonly', False)]}) entry_count = fields.Integer(compute='_entry_count', string='# Asset Entries') name = fields.Char(string='Asset Name', required=True, readonly=True, states={'draft': [('readonly', False)]}) code = fields.Char(string='Reference', size=32, readonly=True, states={'draft': [('readonly', False)]}) value = fields.Float(string='Gross Value', required=True, readonly=True, digits=0, states={'draft': [('readonly', False)]}, oldname='purchase_value') currency_id = fields.Many2one( 'res.currency', string='Currency', required=True, readonly=True, states={'draft': [('readonly', False)]}, default=lambda self: self.env.user.company_id.currency_id.id) company_id = fields.Many2one('res.company', string='Company', required=True, readonly=True, states={'draft': [('readonly', False)]}, default=lambda self: self.env['res.company']. _company_default_get('account.asset.asset')) note = fields.Text() category_id = fields.Many2one('account.asset.category', string='Category', required=True, change_default=True, readonly=True, states={'draft': [('readonly', False)]}) date = fields.Date(string='Date', required=True, readonly=True, states={'draft': [('readonly', False)]}, default=fields.Date.context_today, oldname="purchase_date") state = fields.Selection( [('draft', 'Draft'), ('open', 'Running'), ('close', 'Close')], 'Status', required=True, copy=False, default='draft', help="When an asset is created, the status is 'Draft'.\n" "If the asset is confirmed, the status goes in 'Running' and the depreciation lines can be posted in the accounting.\n" "You can manually close an asset when the depreciation is over. If the last line of depreciation is posted, the asset automatically goes in that status." ) active = fields.Boolean(default=True) partner_id = fields.Many2one('res.partner', string='Partner', readonly=True, states={'draft': [('readonly', False)]}) method = fields.Selection( [('linear', 'Linear'), ('degressive', 'Degressive')], string='Computation Method', required=True, readonly=True, states={'draft': [('readonly', False)]}, default='linear', help= "Choose the method to use to compute the amount of depreciation lines.\n * Linear: Calculated on basis of: Gross Value / Number of Depreciations\n" " * Degressive: Calculated on basis of: Residual Value * Degressive Factor" ) method_number = fields.Integer( string='Number of Depreciations', readonly=True, states={'draft': [('readonly', False)]}, default=5, help="The number of depreciations needed to depreciate your asset") method_period = fields.Integer( string='Number of Months in a Period', required=True, readonly=True, default=12, states={'draft': [('readonly', False)]}, help="The amount of time between two depreciations, in months") method_end = fields.Date(string='Ending Date', readonly=True, states={'draft': [('readonly', False)]}) method_progress_factor = fields.Float( string='Degressive Factor', readonly=True, default=0.3, states={'draft': [('readonly', False)]}) value_residual = fields.Float(compute='_amount_residual', method=True, digits=0, string='Residual Value') method_time = fields.Selection( [('number', 'Number of Depreciations'), ('end', 'Ending Date')], string='Time Method', required=True, readonly=True, default='number', states={'draft': [('readonly', False)]}, help= "Choose the method to use to compute the dates and number of depreciation lines.\n" " * Number of Depreciations: Fix the number of depreciation lines and the time between 2 depreciations.\n" " * Ending Date: Choose the time between 2 depreciations and the date the depreciations won't go beyond." ) prorata = fields.Boolean( string='Prorata Temporis', readonly=True, states={'draft': [('readonly', False)]}, help= 'Indicates that the first depreciation entry for this asset have to be done from the purchase date instead of the first January / Start date of fiscal year' ) depreciation_line_ids = fields.One2many('account.asset.depreciation.line', 'asset_id', string='Depreciation Lines', readonly=True, states={ 'draft': [('readonly', False)], 'open': [('readonly', False)] }) salvage_value = fields.Float( string='Salvage Value', digits=0, readonly=True, states={'draft': [('readonly', False)]}, help="It is the amount you plan to have that you cannot depreciate.") invoice_id = fields.Many2one('account.invoice', string='Invoice', states={'draft': [('readonly', False)]}, copy=False) type = fields.Selection(related="category_id.type", string='Type', required=True) @api.multi def unlink(self): for asset in self: if asset.state in ['open', 'close']: raise UserError( _('You cannot delete a document is in %s state.') % (asset.state, )) if asset.account_move_ids: raise UserError( _('You cannot delete a document that contains posted entries.' )) return super(AccountAssetAsset, self).unlink() @api.multi def _get_last_depreciation_date(self): """ @param id: ids of a account.asset.asset objects @return: Returns a dictionary of the effective dates of the last depreciation entry made for given asset ids. If there isn't any, return the purchase date of this asset """ self.env.cr.execute( """ SELECT a.id as id, COALESCE(MAX(m.date),a.date) AS date FROM account_asset_asset a LEFT JOIN account_move m ON (m.asset_id = a.id) WHERE a.id IN %s GROUP BY a.id, a.date """, (tuple(self.ids), )) result = dict(self.env.cr.fetchall()) return result @api.model def _cron_generate_entries(self): assets = self.env['account.asset.asset'].search([('state', '=', 'open') ]) assets._compute_entries(datetime.today()) def _compute_board_amount(self, sequence, residual_amount, amount_to_depr, undone_dotation_number, posted_depreciation_line_ids, total_days, depreciation_date): amount = 0 if sequence == undone_dotation_number: amount = residual_amount else: if self.method == 'linear': amount = amount_to_depr / (undone_dotation_number - len(posted_depreciation_line_ids)) if self.prorata: amount = amount_to_depr / self.method_number if sequence == 1: days = (self.company_id.compute_fiscalyear_dates( depreciation_date)['date_to'] - depreciation_date).days + 1 amount = (amount_to_depr / self.method_number) / total_days * days elif self.method == 'degressive': amount = residual_amount * self.method_progress_factor if self.prorata: if sequence == 1: days = (self.company_id.compute_fiscalyear_dates( depreciation_date)['date_to'] - depreciation_date).days + 1 amount = (residual_amount * self.method_progress_factor ) / total_days * days return amount def _compute_board_undone_dotation_nb(self, depreciation_date, total_days): undone_dotation_number = self.method_number if self.method_time == 'end': end_date = datetime.strptime(self.method_end, DF).date() undone_dotation_number = 0 while depreciation_date <= end_date: depreciation_date = date( depreciation_date.year, depreciation_date.month, depreciation_date.day) + relativedelta( months=+self.method_period) undone_dotation_number += 1 if self.prorata: undone_dotation_number += 1 return undone_dotation_number @api.multi def compute_depreciation_board(self): self.ensure_one() posted_depreciation_line_ids = self.depreciation_line_ids.filtered( lambda x: x.move_check).sorted(key=lambda l: l.depreciation_date) unposted_depreciation_line_ids = self.depreciation_line_ids.filtered( lambda x: not x.move_check) # Remove old unposted depreciation lines. We cannot use unlink() with One2many field commands = [(2, line_id.id, False) for line_id in unposted_depreciation_line_ids] if self.value_residual != 0.0: amount_to_depr = residual_amount = self.value_residual if self.prorata: depreciation_date = datetime.strptime( self._get_last_depreciation_date()[self.id], DF).date() else: # depreciation_date = 1st of January of purchase year if annual valuation, 1st of # purchase month in other cases if self.method_period >= 12: asset_date = datetime.strptime(self.date[:4] + '-01-01', DF).date() else: asset_date = datetime.strptime(self.date[:7] + '-01', DF).date() # if we already have some previous validated entries, starting date isn't 1st January but last entry + method period if posted_depreciation_line_ids and posted_depreciation_line_ids[ -1].depreciation_date: last_depreciation_date = datetime.strptime( posted_depreciation_line_ids[-1].depreciation_date, DF).date() depreciation_date = last_depreciation_date + relativedelta( months=+self.method_period) else: depreciation_date = asset_date day = depreciation_date.day month = depreciation_date.month year = depreciation_date.year total_days = (year % 4) and 365 or 366 undone_dotation_number = self._compute_board_undone_dotation_nb( depreciation_date, total_days) for x in range(len(posted_depreciation_line_ids), undone_dotation_number): sequence = x + 1 amount = self._compute_board_amount( sequence, residual_amount, amount_to_depr, undone_dotation_number, posted_depreciation_line_ids, total_days, depreciation_date) amount = self.currency_id.round(amount) residual_amount -= amount vals = { 'amount': amount, 'asset_id': self.id, 'sequence': sequence, 'name': (self.code or '') + '/' + str(sequence), 'remaining_value': residual_amount, 'depreciated_value': self.value - (self.salvage_value + residual_amount), 'depreciation_date': depreciation_date.strftime(DF), } commands.append((0, False, vals)) # Considering Depr. Period as months depreciation_date = date(year, month, day) + relativedelta( months=+self.method_period) day = depreciation_date.day month = depreciation_date.month year = depreciation_date.year self.write({'depreciation_line_ids': commands}) return True @api.multi def validate(self): self.write({'state': 'open'}) fields = [ 'method', 'method_number', 'method_period', 'method_end', 'method_progress_factor', 'method_time', 'salvage_value', 'invoice_id', ] ref_tracked_fields = self.env['account.asset.asset'].fields_get(fields) for asset in self: tracked_fields = ref_tracked_fields.copy() if asset.method == 'linear': del (tracked_fields['method_progress_factor']) if asset.method_time != 'end': del (tracked_fields['method_end']) else: del (tracked_fields['method_number']) dummy, tracking_value_ids = asset._message_track( tracked_fields, dict.fromkeys(fields)) asset.message_post(subject=_('Asset created'), tracking_value_ids=tracking_value_ids) @api.multi def set_to_close(self): move_ids = [] for asset in self: unposted_depreciation_line_ids = asset.depreciation_line_ids.filtered( lambda x: not x.move_check) if unposted_depreciation_line_ids: old_values = { 'method_end': asset.method_end, 'method_number': asset.method_number, } # Remove all unposted depr. lines commands = [(2, line_id.id, False) for line_id in unposted_depreciation_line_ids] # Create a new depr. line with the residual amount and post it sequence = len(asset.depreciation_line_ids) - len( unposted_depreciation_line_ids) + 1 today = datetime.today().strftime(DF) vals = { 'amount': asset.value_residual, 'asset_id': asset.id, 'sequence': sequence, 'name': (asset.code or '') + '/' + str(sequence), 'remaining_value': 0, 'depreciated_value': asset.value - asset.salvage_value, # the asset is completely depreciated 'depreciation_date': today, } commands.append((0, False, vals)) asset.write({ 'depreciation_line_ids': commands, 'method_end': today, 'method_number': sequence }) tracked_fields = self.env['account.asset.asset'].fields_get( ['method_number', 'method_end']) changes, tracking_value_ids = asset._message_track( tracked_fields, old_values) if changes: asset.message_post(subject=_( 'Asset sold or disposed. Accounting entry awaiting for validation.' ), tracking_value_ids=tracking_value_ids) move_ids += asset.depreciation_line_ids[-1].create_move( post_move=False) if move_ids: name = _('Disposal Move') view_mode = 'form' if len(move_ids) > 1: name = _('Disposal Moves') view_mode = 'tree,form' return { 'name': name, 'view_type': 'form', 'view_mode': view_mode, 'res_model': 'account.move', 'type': 'ir.actions.act_window', 'target': 'current', 'res_id': move_ids[0], } @api.multi def set_to_draft(self): self.write({'state': 'draft'}) @api.one @api.depends('value', 'salvage_value', 'depreciation_line_ids.move_check', 'depreciation_line_ids.amount') def _amount_residual(self): total_amount = 0.0 for line in self.depreciation_line_ids: if line.move_check: total_amount += line.amount self.value_residual = self.value - total_amount - self.salvage_value @api.onchange('company_id') def onchange_company_id(self): self.currency_id = self.company_id.currency_id.id @api.multi @api.depends('account_move_ids') def _entry_count(self): for asset in self: asset.entry_count = self.env['account.move'].search_count([ ('asset_id', '=', asset.id) ]) @api.one @api.constrains('prorata', 'method_time') def _check_prorata(self): if self.prorata and self.method_time != 'number': raise ValidationError( _('Prorata temporis can be applied only for time method "number of depreciations".' )) @api.onchange('category_id') def onchange_category_id(self): vals = self.onchange_category_id_values(self.category_id.id) # We cannot use 'write' on an object that doesn't exist yet if vals: for k, v in vals['value'].iteritems(): setattr(self, k, v) def onchange_category_id_values(self, category_id): if category_id: category = self.env['account.asset.category'].browse(category_id) return { 'value': { 'method': category.method, 'method_number': category.method_number, 'method_time': category.method_time, 'method_period': category.method_period, 'method_progress_factor': category.method_progress_factor, 'method_end': category.method_end, 'prorata': category.prorata, } } @api.onchange('method_time') def onchange_method_time(self): if self.method_time != 'number': self.prorata = False @api.multi def copy_data(self, default=None): if default is None: default = {} default['name'] = self.name + _(' (copy)') return super(AccountAssetAsset, self).copy_data(default)[0] @api.multi def _compute_entries(self, date): depreciation_ids = self.env['account.asset.depreciation.line'].search([ ('asset_id', 'in', self.ids), ('depreciation_date', '<=', date), ('move_check', '=', False) ]) return depreciation_ids.create_move() @api.model def create(self, vals): asset = super(AccountAssetAsset, self.with_context(mail_create_nolog=True)).create(vals) asset.compute_depreciation_board() return asset @api.multi def write(self, vals): res = super(AccountAssetAsset, self).write(vals) if 'depreciation_line_ids' not in vals and 'state' not in vals: for rec in self: rec.compute_depreciation_board() return res @api.multi def open_entries(self): return { 'name': _('Journal Entries'), 'view_type': 'form', 'view_mode': 'tree,form', 'res_model': 'account.move', 'view_id': False, 'type': 'ir.actions.act_window', 'context': dict(self.env.context or {}, search_default_asset_id=self.id, default_asset_id=self.id), }
class ReportIntrastatCode(models.Model): _name = "report.intrastat.code" _description = "H.S. Code" _order = "name" _rec_name = "display_name" @api.one @api.depends('name', 'description') def _compute_display_name(self): display_name = self.name or '' if self.description: display_name += ' ' + self.description self.display_name = display_name name = fields.Char( string='H.S. code', help="Full length Harmonized System code (digits only). Full list is " "available from the World Customs Organisation, see " "http://www.wcoomd.org") description = fields.Char( 'Description', help="Short text description of the H.S. category") display_name = fields.Char(compute='_compute_display_name', string="Display Name", readonly=True, store=True) intrastat_code = fields.Char( string='European Intrastat Code', size=9, required=True, help="Code used for the Intrastat declaration. Must be part " "of the 'Combined Nomenclature' (CN), cf " "http://en.wikipedia.org/wiki/Combined_Nomenclature" "Must have 8 digits with sometimes a 9th digit.") intrastat_uom_id = fields.Many2one( 'product.uom', string='UoM for Intrastat Report', help="Select the unit of measure if one is required for " "this particular Intrastat Code (other than the weight in Kg). " "If no particular unit of measure is required, leave empty.") active = fields.Boolean(default=True) product_categ_ids = fields.One2many('product.category', 'intrastat_id', string='Product Categories') product_tmpl_ids = fields.One2many('product.template', 'intrastat_id', string='Products') @api.constrains('name', 'intrastat_code') def _hs_code(self): if self.name and not self.name.isdigit(): raise ValidationError( _("H.S. codes should only contain digits. It is not the case " "of H.S. code '%s'.") % self.name) if self.intrastat_code and not self.intrastat_code.isdigit(): raise ValidationError( _("The field Intrastat Code should only contain digits. " "It is not the case of Intrastat Code '%s'.") % self.intrastat_code) if self.intrastat_code and len(self.intrastat_code) not in (8, 9): raise ValidationError( _("The field Intrastat Code should " "contain 8 or 9 digits. It is not the case of " "Intrastat Code '%s'.") % self.intrastat_code) _sql_constraints = [('hs_code_uniq', 'unique(name)', 'This H.S. code already exists in Odoo !')] @api.model @api.returns('self', lambda value: value.id) def create(self, vals): if vals.get('intrastat_code'): vals['intrastat_code'] = vals['intrastat_code'].replace(' ', '') return super(ReportIntrastatCode, self).create(vals) @api.multi def write(self, vals): if vals.get('intrastat_code'): vals['intrastat_code'] = vals['intrastat_code'].replace(' ', '') return super(ReportIntrastatCode, self).write(vals)
class account_voucher(models.Model): _inherit = 'account.voucher' received_third_check_ids = fields.One2many( 'account.check', 'voucher_id', 'Third Checks', domain=[('type', '=', 'third')], context={ 'default_type': 'third', 'from_voucher': True }, required=False, readonly=True, copy=False, states={'draft': [('readonly', False)]}) issued_check_ids = fields.One2many('account.check', 'voucher_id', 'Issued Checks', domain=[('type', '=', 'issue')], context={ 'default_type': 'issue', 'from_voucher': True }, copy=False, required=False, readonly=True, states={'draft': [('readonly', False)]}) delivered_third_check_ids = fields.One2many( 'account.check', 'third_handed_voucher_id', 'Third Checks', domain=[('type', '=', 'third')], copy=False, context={'from_voucher': True}, required=False, readonly=True, states={'draft': [('readonly', False)]}) validate_only_checks = fields.Boolean( related='journal_id.validate_only_checks', string='Validate only Checks', readonly=True, ) check_type = fields.Selection( related='journal_id.check_type', string='Check Type', readonly=True, ) dummy_journal_id = fields.Many2one( related='journal_id', readonly=True, string='Dummy Journa', help='Field used for new api onchange methods over journal', ) amount_readonly = fields.Float( related='amount', string='Total', digits_compute=dp.get_precision('Account'), readonly=True, ) @api.onchange('dummy_journal_id') def change_dummy_journal_id(self): """Unlink checks on journal change""" self.delivered_third_check_ids = False self.issued_check_ids = False self.received_third_check_ids = False @api.multi def action_cancel_draft(self): res = super(account_voucher, self).action_cancel_draft() checks = self.env['account.check'].search([('voucher_id', 'in', self.ids)]) checks.action_cancel_draft() return res @api.model def first_move_line_get(self, voucher_id, move_id, company_currency, current_currency): vals = super(account_voucher, self).first_move_line_get(voucher_id, move_id, company_currency, current_currency) voucher = self.browse(voucher_id) if company_currency != current_currency and voucher.amount: debit = vals.get('debit') credit = vals.get('credit') total = debit - credit exchange_rate = total / voucher.amount checks = [] if voucher.check_type == 'third': checks = voucher.received_third_check_ids elif voucher.check_type == 'issue': checks = voucher.issued_check_ids for check in checks: company_currency_amount = abs(check.amount * exchange_rate) check.company_currency_amount = company_currency_amount return vals @api.multi def cancel_voucher(self): for voucher in self: for check in voucher.received_third_check_ids: if check.state not in ['draft', 'holding']: raise Warning( _('You can not cancel a voucher thas has received third checks in states other than "draft or "holding". First try to change check state.' )) for check in voucher.issued_check_ids: if check.state not in ['draft', 'handed']: raise Warning( _('You can not cancel a voucher thas has issue checks in states other than "draft or "handed". First try to change check state.' )) for check in voucher.delivered_third_check_ids: if check.state not in ['handed']: raise Warning( _('You can not cancel a voucher thas has delivered checks in states other than "handed". First try to change check state.' )) res = super(account_voucher, self).cancel_voucher() checks = self.env['account.check'].search([ '|', ('voucher_id', 'in', self.ids), ('third_handed_voucher_id', 'in', self.ids) ]) for check in checks: check.signal_workflow('cancel') return res def proforma_voucher(self, cr, uid, ids, context=None): res = super(account_voucher, self).proforma_voucher(cr, uid, ids, context=None) for voucher in self.browse(cr, uid, ids, context=context): if voucher.type == 'payment': for check in voucher.issued_check_ids: check.signal_workflow('draft_router') for check in voucher.delivered_third_check_ids: check.signal_workflow('holding_handed') elif voucher.type == 'receipt': for check in voucher.received_third_check_ids: check.signal_workflow('draft_router') return res @api.one @api.onchange('amount_readonly') def onchange_amount_readonly(self): self.amount = self.amount_readonly @api.one @api.onchange('received_third_check_ids', 'issued_check_ids') def onchange_customer_checks(self): self.amount_readonly = sum(x.amount for x in self.received_third_check_ids) @api.one @api.onchange('delivered_third_check_ids', 'issued_check_ids') def onchange_supplier_checks(self): amount = sum(x.amount for x in self.delivered_third_check_ids) amount += sum(x.amount for x in self.issued_check_ids) self.amount_readonly = amount
class settlement_agent(models.Model): """Agent's settlement""" _name = 'settlement.agent' def _invoice_line_hook(self, cursor, user, move_line, invoice_line_id, context=None): '''Call after the creation of the invoice line''' return def _get_address_invoice(self, cr, uid, settlement, context=None): """Return { 'default: address, 'contact': address, 'invoice': address } for invoice """ partner_obj = self.pool['res.partner'] partner = settlement.agent_id.partner_id return partner_obj.address_get(cr, uid, [partner.id], ['default', 'contact', 'invoice'], context=context) def _invoice_hook(self, cr, uid, picking, invoice_id, context=None): '''Call after the creation of the invoice''' return agent_id = fields.Many2one("sale.agent", string="Agent", required=True, select=1) total_per = fields.Float(string="Total percentages", readonly=True) total_sections = fields.Float(string="Total sections", readonly=True) total = fields.Float(string="Total", readonly=True) lines = fields.One2many("settlement.line", "settlement_agent_id", string="Lines", readonly=True) invoices = fields.One2many("settled.invoice.agent", "settlement_agent_id", string="Invoices", readonly=True) settlement_id = fields.Many2one("settlement", string="Settlement", required=True, ondelete="cascade") def get_currency_id(self, cr, uid, picking, context=None): return False def action_invoice_create(self, cr, uid, ids, journal_id, product_id, context=None): '''Return ids of created invoices for the settlements''' invoice_obj = self.pool['account.invoice'] invoice_line_obj = self.pool['account.invoice.line'] product_pool = self.pool['product.product'] account_fiscal_position_pool = self.pool['account.fiscal.position'] res = {} for settlement in self.browse(cr, uid, ids, context=context): payment_term_id = False partner = settlement.agent_id and settlement.agent_id.partner_id if not partner: raise exceptions.Warning( _("Agent to settle hasn't assigned partner.")) # Invoice is from a supplier account_id = partner.property_account_payable.id address_default_id, address_contact_id, address_invoice_id = ( self._get_address_invoice(cr, uid, settlement, context=context).values()) # Don't group invoice_vals = { 'name': settlement.settlement_id.name, 'origin': (settlement.settlement_id.name or ''), 'type': 'in_invoice', 'account_id': account_id, 'partner_id': partner.id, 'address_invoice_id': address_invoice_id, 'address_contact_id': address_contact_id, 'payment_term': payment_term_id, 'fiscal_position': partner.property_account_position.id } cur_id = self.get_currency_id(cr, uid, settlement, context=context) if cur_id: invoice_vals['currency_id'] = cur_id if journal_id: invoice_vals['journal_id'] = journal_id invoice_id = invoice_obj.create(cr, uid, invoice_vals, context=context) res[settlement.id] = invoice_id # The product is chosen in the appropriate wizard product = product_pool.browse(cr, uid, product_id, context=context) account_id = product.product_tmpl_id.property_account_expense.id if not account_id: account_id = product.categ_id.property_account_expense_categ.id # Tax calculations to be applied taxes = [] if product.supplier_taxes_id: taxes.append(product.supplier_taxes_id) # Append the retention associated to the agent if settlement.agent_id and settlement.agent_id.retention_id: taxes.append(settlement.agent_id.retention_id) if settlement.agent_id and settlement.agent_id.partner_id: tax_ids = self.pool.get('account.fiscal.position').map_tax( cr, uid, settlement.agent_id.partner_id.property_account_position, taxes) else: tax_ids = map(lambda x: x.id, taxes) for invoice in settlement.invoices: origin = invoice.invoice_number name = invoice.invoice_number price_unit = invoice.settled_amount discount = 0 # set UoS if it's a sale and the picking doesn't have one uos_id = False account_id = account_fiscal_position_pool.map_account( cr, uid, partner.property_account_position, account_id, context=context) invoice_line_id = invoice_line_obj.create( cr, uid, { 'name': name, 'origin': origin, 'invoice_id': invoice_id, 'uos_id': uos_id, 'product_id': product.id, 'account_id': account_id, 'price_unit': price_unit, 'discount': discount, 'quantity': 1, 'invoice_line_tax_id': [(6, 0, tax_ids)], }, context=context) self._invoice_line_hook(cr, uid, invoice, invoice_line_id, context=context) invoice_obj.button_compute(cr, uid, [invoice_id], context=context, set_total=(type in ('in_invoice', 'in_refund'))) self._invoice_hook(cr, uid, settlement, invoice_id, context=context) return res def calculate(self, cr, uid, ids, date_from, date_to, context=None): settlement_line_pool = self.pool['settlement.line'] invoice_line_agent_pool = self.pool['invoice.line.agent'] set_agent = self.browse(cr, uid, ids, context=context) user = self.pool['res.users'].browse(cr, uid, uid, context=context) # Recalculate all the line that has commission sql = """ SELECT invoice_line_agent.id FROM account_invoice_line INNER JOIN invoice_line_agent ON invoice_line_agent.invoice_line_id=account_invoice_line.id INNER JOIN account_invoice ON account_invoice_line.invoice_id = account_invoice.id WHERE invoice_line_agent.agent_id={} AND invoice_line_agent.settled=True AND account_invoice.state<>'draft' AND account_invoice.type='out_invoice' AND account_invoice.date_invoice >= '{}' AND account_invoice.date_invoice <= '{}' AND account_invoice.company_id = {} """.format(set_agent.agent_id.id, date_from, date_to, user.company_id.id) cr.execute(sql) res = cr.fetchall() inv_line_agent_ids = [x[0] for x in res] invoice_line_agent_pool.calculate_commission(cr, uid, inv_line_agent_ids, context=context) sql = """ SELECT account_invoice_line.id FROM account_invoice_line INNER JOIN invoice_line_agent ON invoice_line_agent.invoice_line_id=account_invoice_line.id INNER JOIN account_invoice ON account_invoice_line.invoice_id = account_invoice.id WHERE invoice_line_agent.agent_id={} AND invoice_line_agent.settled=False AND account_invoice.state<>'draft' AND account_invoice.type='out_invoice' AND account_invoice.date_invoice >= '{}' AND account_invoice.date_invoice <= '{}' AND account_invoice.company_id = {}""".format( set_agent.agent_id.id, date_from, date_to, user.company_id.id) cr.execute(sql) res = cr.fetchall() inv_line_ids = [x[0] for x in res] total_per = 0 total_sections = 0 sections = {} for inv_line_id in inv_line_ids: linea_id = settlement_line_pool.create( cr, uid, { 'invoice_line_id': inv_line_id, 'settlement_agent_id': ids }, context=context) settlement_line_pool.calculate(cr, uid, linea_id, context=context) line = settlement_line_pool.browse(cr, uid, linea_id, context=context) # Mark the commission in the invoice as settled # and calculate the quantity # If we use sections then the quantity is zero, # although will reflect the Agent if line.commission_id.commission_type == "fixed": total_per = total_per + line.commission inv_ag_ids = invoice_line_agent_pool.search( cr, uid, [('invoice_line_id', '=', inv_line_id), ('agent_id', '=', set_agent.agent_id.id)], context=context) invoice_line_agent_pool.write(cr, uid, inv_ag_ids, { 'settled': True, 'quantity': line.commission }, context=context) product_id = line.invoice_line_id.product_id is_commission_free = product_id.commission_free if (line.commission_id.commission_type == "section" and not is_commission_free): # We aggregate the base value by grouping # by the distinct sections that the agent # has assigned for it if line.commission_id.id in sections: sections[line.commission_id.id]['base'] = ( sections[line.commission_id.id]['base'] + line.invoice_line_id.price_subtotal) # Append the lines for the calculation by sections sections[line.commission_id.id]['lines'].append(line) else: sections[line.commission_id.id] = { 'type': line.commission_id, 'base': line.invoice_line_id.price_subtotal, 'lines': [line] } # Iterate over each section created for tramo in sections: # Calculate the commision for each section tramo = sections[tramo] sections[tramo].update( {'commission': tramo['type'].calcula_tramos(tramo['base'])}) total_sections = total_sections + sections[tramo]['commission'] # Split the commision for each line for linea_tramo in sections[tramo]['lines']: subtotal = linea_tramo.invoice_line_id.price_subtotal com_por_linea = (sections[tramo]['commission'] * (subtotal / sections[tramo]['base'])) linea_tramo.write({'commission': com_por_linea}) inv_ag_ids = invoice_line_agent_pool.search( cr, uid, [('invoice_line_id', '=', linea_tramo.invoice_line_id.id), ('agent_id', '=', set_agent.agent_id.id)], context=context) invoice_line_agent_pool.write(cr, uid, inv_ag_ids, { 'settled': True, 'quantity': com_por_linea }, context=context) total = total_per + total_sections self.write(cr, uid, ids, { 'total_per': total_per, 'total_sections': total_sections, 'total': total }, context=context)
class ReportAccountFinancialReport(models.Model): _name = "account.financial.html.report" _description = "Account Report" name = fields.Char(translate=True) debit_credit = fields.Boolean('Show Credit and Debit Columns') line_ids = fields.One2many('account.financial.html.report.line', 'financial_report_id', string='Lines') report_type = fields.Selection( [('date_range', 'Based on date ranges'), ('date_range_extended', "Based on date ranges with 'older' and 'total' columns and last 3 months" ), ('no_date_range', 'Based on a single date'), ('date_range_cash', 'Bases on date ranges and cash basis method')], string='Analysis Periods', default=False, required=True, help= 'For report like the balance sheet that do not work with date ranges') company_id = fields.Many2one('res.company', string='Company') menuitem_created = fields.Boolean("Menu Has Been Created", default=False) parent_id = fields.Many2one('ir.ui.menu') def create_action_and_menu(self, parent_id): client_action = self.env['ir.actions.client'].create({ 'name': self.get_title(), 'tag': 'account_report_generic', 'context': { 'url': '/account_reports/output_format/financial_report/' + str(self.id), 'model': 'account.financial.html.report', 'id': self.id, }, }) self.env['ir.ui.menu'].create({ 'name': self.get_title(), 'parent_id': parent_id or self.env['ir.model.data'].xmlid_to_res_id( 'account.menu_finance_reports'), 'action': 'ir.actions.client,%s' % (client_action.id, ), }) self.write({'menuitem_created': True}) @api.model def create(self, vals): parent_id = False if vals.get('parent_id'): parent_id = vals['parent_id'] del vals['parent_id'] res = super(ReportAccountFinancialReport, self).create(vals) res.create_action_and_menu(parent_id) return res @api.multi def get_lines(self, context_id, line_id=None): if isinstance(context_id, int): context_id = self.env[ 'account.financial.html.report.context'].browse(context_id) line_obj = self.line_ids if line_id: line_obj = self.env['account.financial.html.report.line'].search([ ('id', '=', line_id) ]) if context_id.comparison: line_obj = line_obj.with_context( periods=context_id.get_cmp_periods()) used_currency = self.env.user.company_id.currency_id currency_table = {} for company in self.env['res.company'].search([]): if company.currency_id != used_currency: currency_table[ company.currency_id. id] = used_currency.rate / company.currency_id.rate linesDicts = [{} for _ in context_id.get_periods()] res = line_obj.with_context( state=context_id.all_entries and 'all' or 'posted', cash_basis=self.report_type == 'date_range_cash' or context_id.cash_basis, strict_range=self.report_type == 'date_range_extended', aged_balance=self.report_type == 'date_range_extended', company_ids=context_id.company_ids.ids, context=context_id).get_lines(self, context_id, currency_table, linesDicts) return res def get_title(self): return self.name def get_name(self): return 'financial_report' @api.multi def get_report_type(self): return self.report_type def get_template(self): return 'account_reports.report_financial'
class SaleOrder(models.Model): _name = "sale.order" _inherit = ['mail.thread', 'ir.needaction_mixin'] _description = "Sales Order" _order = 'date_order desc, id desc' @api.depends('order_line.price_total') def _amount_all(self): """ Compute the total amounts of the SO. """ for order in self: amount_untaxed = amount_tax = 0.0 for line in order.order_line: amount_untaxed += line.price_subtotal amount_tax += line.price_tax order.update({ 'amount_untaxed': order.pricelist_id.currency_id.round(amount_untaxed), 'amount_tax': order.pricelist_id.currency_id.round(amount_tax), 'amount_total': amount_untaxed + amount_tax, }) @api.depends('state', 'order_line.invoice_status') def _get_invoiced(self): """ Compute the invoice status of a SO. Possible statuses: - no: if the SO is not in status 'sale' or 'done', we consider that there is nothing to invoice. This is also hte default value if the conditions of no other status is met. - to invoice: if any SO line is 'to invoice', the whole SO is 'to invoice' - invoiced: if all SO lines are invoiced, the SO is invoiced. - upselling: if all SO lines are invoiced or upselling, the status is upselling. The invoice_ids are obtained thanks to the invoice lines of the SO lines, and we also search for possible refunds created directly from existing invoices. This is necessary since such a refund is not directly linked to the SO. """ for order in self: invoice_ids = order.order_line.mapped('invoice_lines').mapped( 'invoice_id') # Search for refunds as well refund_ids = self.env['account.invoice'].browse() if invoice_ids: refund_ids = refund_ids.search([ ('type', '=', 'out_refund'), ('origin', 'in', invoice_ids.mapped('number')) ]) line_invoice_status = [ line.invoice_status for line in order.order_line ] if order.state not in ('sale', 'done'): invoice_status = 'no' elif any(invoice_status == 'to invoice' for invoice_status in line_invoice_status): invoice_status = 'to invoice' elif all(invoice_status == 'invoiced' for invoice_status in line_invoice_status): invoice_status = 'invoiced' elif all(invoice_status in ['invoiced', 'upselling'] for invoice_status in line_invoice_status): invoice_status = 'upselling' else: invoice_status = 'no' order.update({ 'invoice_count': len(set(invoice_ids.ids + refund_ids.ids)), 'invoice_ids': invoice_ids.ids + refund_ids.ids, 'invoice_status': invoice_status }) @api.model def _default_note(self): return self.env.user.company_id.sale_note @api.model def _get_default_team(self): default_team_id = self.env['crm.team']._get_default_team_id() return self.env['crm.team'].browse(default_team_id) @api.onchange('fiscal_position_id') def _compute_tax_id(self): """ Trigger the recompute of the taxes if the fiscal position is changed on the SO. """ for order in self: order.order_line._compute_tax_id() name = fields.Char(string='Order Reference', required=True, copy=False, readonly=True, index=True, default='New') origin = fields.Char( string='Source Document', help= "Reference of the document that generated this sales order request.") client_order_ref = fields.Char(string='Customer Reference', copy=False) state = fields.Selection([ ('draft', 'Quotation'), ('sent', 'Quotation Sent'), ('sale', 'Sale Order'), ('done', 'Done'), ('cancel', 'Cancelled'), ], string='Status', readonly=True, copy=False, index=True, track_visibility='onchange', default='draft') date_order = fields.Datetime(string='Order Date', required=True, readonly=True, index=True, states={ 'draft': [('readonly', False)], 'sent': [('readonly', False)] }, copy=False, default=fields.Datetime.now) validity_date = fields.Date(string='Expiration Date', readonly=True, states={ 'draft': [('readonly', False)], 'sent': [('readonly', False)] }) create_date = fields.Datetime(string='Creation Date', readonly=True, index=True, help="Date on which sales order is created.") user_id = fields.Many2one('res.users', string='Salesperson', index=True, track_visibility='onchange', default=lambda self: self.env.user) partner_id = fields.Many2one('res.partner', string='Customer', readonly=True, states={ 'draft': [('readonly', False)], 'sent': [('readonly', False)] }, required=True, change_default=True, index=True, track_visibility='always') partner_invoice_id = fields.Many2one( 'res.partner', string='Invoice Address', readonly=True, required=True, states={ 'draft': [('readonly', False)], 'sent': [('readonly', False)] }, help="Invoice address for current sales order.") partner_shipping_id = fields.Many2one( 'res.partner', string='Delivery Address', readonly=True, required=True, states={ 'draft': [('readonly', False)], 'sent': [('readonly', False)] }, help="Delivery address for current sales order.") pricelist_id = fields.Many2one('product.pricelist', string='Pricelist', required=True, readonly=True, states={ 'draft': [('readonly', False)], 'sent': [('readonly', False)] }, help="Pricelist for current sales order.") currency_id = fields.Many2one("res.currency", related='pricelist_id.currency_id', string="Currency", readonly=True, required=True) project_id = fields.Many2one( 'account.analytic.account', 'Analytic Account', readonly=True, states={ 'draft': [('readonly', False)], 'sent': [('readonly', False)] }, help="The analytic account related to a sales order.", copy=False) order_line = fields.One2many('sale.order.line', 'order_id', string='Order Lines', states={ 'cancel': [('readonly', True)], 'done': [('readonly', True)] }, copy=True) invoice_count = fields.Integer(string='# of Invoices', compute='_get_invoiced', readonly=True) invoice_ids = fields.Many2many("account.invoice", string='Invoices', compute="_get_invoiced", readonly=True, copy=False) invoice_status = fields.Selection([('upselling', 'Upselling Opportunity'), ('invoiced', 'Fully Invoiced'), ('to invoice', 'To Invoice'), ('no', 'Nothing to Invoice')], string='Invoice Status', compute='_get_invoiced', store=True, readonly=True, default='no') note = fields.Text('Terms and conditions', default=_default_note) amount_untaxed = fields.Monetary(string='Untaxed Amount', store=True, readonly=True, compute='_amount_all', track_visibility='always') amount_tax = fields.Monetary(string='Taxes', store=True, readonly=True, compute='_amount_all', track_visibility='always') amount_total = fields.Monetary(string='Total', store=True, readonly=True, compute='_amount_all', track_visibility='always') payment_term_id = fields.Many2one('account.payment.term', string='Payment Term', oldname='payment_term') fiscal_position_id = fields.Many2one('account.fiscal.position', oldname='fiscal_position', string='Fiscal Position') company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env['res.company']. _company_default_get('sale.order')) team_id = fields.Many2one('crm.team', 'Sales Team', change_default=True, default=_get_default_team) procurement_group_id = fields.Many2one('procurement.group', 'Procurement Group', copy=False) product_id = fields.Many2one('product.product', related='order_line.product_id', string='Product') @api.multi def button_dummy(self): return True @api.multi def unlink(self): for order in self: if order.state != 'draft': raise UserError(_('You can only delete draft quotations!')) return super(SaleOrder, self).unlink() @api.multi def _track_subtype(self, init_values): self.ensure_one() if 'state' in init_values and self.state == 'sale': return 'sale.mt_order_confirmed' elif 'state' in init_values and self.state == 'sent': return 'sale.mt_order_sent' return super(SaleOrder, self)._track_subtype(init_values) @api.multi @api.onchange('partner_shipping_id') def onchange_partner_shipping_id(self): """ Trigger the change of fiscal position when the shipping address is modified. """ fiscal_position = self.env[ 'account.fiscal.position'].get_fiscal_position( self.partner_id.id, self.partner_shipping_id.id) if fiscal_position: self.fiscal_position_id = fiscal_position return {} @api.multi @api.onchange('partner_id') def onchange_partner_id(self): """ Update the following fields when the partner is changed: - Pricelist - Payment term - Invoice address - Delivery address """ if not self.partner_id: self.update({ 'partner_invoice_id': False, 'partner_shipping_id': False, 'payment_term_id': False, 'fiscal_position_id': False, }) return addr = self.partner_id.address_get(['delivery', 'invoice']) values = { 'pricelist_id': self.partner_id.property_product_pricelist and self.partner_id.property_product_pricelist.id or False, 'payment_term_id': self.partner_id.property_payment_term_id and self.partner_id.property_payment_term_id.id or False, 'partner_invoice_id': addr['invoice'], 'partner_shipping_id': addr['delivery'], 'note': self.with_context( lang=self.partner_id.lang).env.user.company_id.sale_note, } if self.partner_id.user_id: values['user_id'] = self.partner_id.user_id.id if self.partner_id.team_id: values['team_id'] = self.partner_id.team_id.id self.update(values) @api.model def create(self, vals): if vals.get('name', 'New') == 'New': vals['name'] = self.env['ir.sequence'].next_by_code( 'sale.order') or 'New' # Makes sure partner_invoice_id', 'partner_shipping_id' and 'pricelist_id' are defined if any(f not in vals for f in ['partner_invoice_id', 'partner_shipping_id', 'pricelist_id']): partner = self.env['res.partner'].browse(vals.get('partner_id')) addr = partner.address_get(['delivery', 'invoice']) vals['partner_invoice_id'] = vals.setdefault( 'partner_invoice_id', addr['invoice']) vals['partner_shipping_id'] = vals.setdefault( 'partner_shipping_id', addr['delivery']) vals['pricelist_id'] = vals.setdefault( 'pricelist_id', partner.property_product_pricelist and partner.property_product_pricelist.id) result = super(SaleOrder, self).create(vals) return result @api.multi def _prepare_invoice(self): """ Prepare the dict of values to create the new invoice for a sales order. This method may be overridden to implement custom invoice generation (making sure to call super() to establish a clean extension chain). """ self.ensure_one() journal_id = self.env['account.invoice'].default_get(['journal_id' ])['journal_id'] if not journal_id: raise UserError( _('Please define an accounting sale journal for this company.') ) invoice_vals = { 'name': self.client_order_ref or '', 'origin': self.name, 'type': 'out_invoice', 'reference': self.client_order_ref or self.name, 'account_id': self.partner_invoice_id.property_account_receivable_id.id, 'partner_id': self.partner_invoice_id.id, 'journal_id': journal_id, 'currency_id': self.pricelist_id.currency_id.id, 'comment': self.note, 'payment_term_id': self.payment_term_id.id, 'fiscal_position_id': self.fiscal_position_id.id or self.partner_invoice_id.property_account_position_id.id, 'company_id': self.company_id.id, 'user_id': self.user_id and self.user_id.id, 'team_id': self.team_id.id } return invoice_vals @api.multi def print_quotation(self): self.filtered(lambda s: s.state == 'draft').write({'state': 'sent'}) return self.env['report'].get_action(self, 'sale.report_saleorder') @api.multi def action_view_invoice(self): invoice_ids = self.mapped('invoice_ids') imd = self.env['ir.model.data'] action = imd.xmlid_to_object('account.action_invoice_tree1') list_view_id = imd.xmlid_to_res_id('account.invoice_tree') form_view_id = imd.xmlid_to_res_id('account.invoice_form') result = { 'name': action.name, 'help': action.help, 'type': action.type, 'views': [[list_view_id, 'tree'], [form_view_id, 'form'], [False, 'graph'], [False, 'kanban'], [False, 'calendar'], [False, 'pivot']], 'target': action.target, 'context': action.context, 'res_model': action.res_model, } if len(invoice_ids) > 1: result['domain'] = "[('id','in',%s)]" % invoice_ids.ids elif len(invoice_ids) == 1: result['views'] = [(form_view_id, 'form')] result['res_id'] = invoice_ids.ids[0] else: result = {'type': 'ir.actions.act_window_close'} return result @api.multi def action_invoice_create(self, grouped=False, final=False): """ Create the invoice associated to the SO. :param grouped: if True, invoices are grouped by SO id. If False, invoices are grouped by (partner, currency) :param final: if True, refunds will be generated if necessary :returns: list of created invoices """ inv_obj = self.env['account.invoice'] precision = self.env['decimal.precision'].precision_get( 'Product Unit of Measure') invoices = {} for order in self: group_key = order.id if grouped else (order.partner_id.id, order.currency_id.id) for line in order.order_line.sorted( key=lambda l: l.qty_to_invoice < 0): if float_is_zero(line.qty_to_invoice, precision_digits=precision): continue if group_key not in invoices: inv_data = order._prepare_invoice() invoice = inv_obj.create(inv_data) invoices[group_key] = invoice elif group_key in invoices and order.name not in invoices[ group_key].origin.split(', '): invoices[group_key].write({ 'origin': invoices[group_key].origin + ', ' + order.name }) if line.qty_to_invoice > 0: line.invoice_line_create(invoices[group_key].id, line.qty_to_invoice) elif line.qty_to_invoice < 0 and final: line.invoice_line_create(invoices[group_key].id, line.qty_to_invoice) for invoice in invoices.values(): # If invoice is negative, do a refund invoice instead if invoice.amount_untaxed < 0: invoice.type = 'out_refund' for line in invoice.invoice_line_ids: line.quantity = -line.quantity # Necessary to force computation of taxes. In account_invoice, they are triggered # by onchanges, which are not triggered when doing a create. invoice.compute_taxes() return [inv.id for inv in invoices.values()] @api.multi def action_draft(self): self.filtered(lambda s: s.state in ['cancel', 'sent']).write( {'state': 'draft'}) @api.multi def action_cancel(self): self.write({'state': 'cancel'}) @api.multi def action_quotation_send(self): ''' This function opens a window to compose an email, with the edi sale template message loaded by default ''' self.ensure_one() ir_model_data = self.env['ir.model.data'] try: template_id = ir_model_data.get_object_reference( 'sale', 'email_template_edi_sale')[1] except ValueError: template_id = False try: compose_form_id = ir_model_data.get_object_reference( 'mail', 'email_compose_message_wizard_form')[1] except ValueError: compose_form_id = False ctx = dict() ctx.update({ 'default_model': 'sale.order', 'default_res_id': self.ids[0], 'default_use_template': bool(template_id), 'default_template_id': template_id, 'default_composition_mode': 'comment', 'mark_so_as_sent': True }) return { '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.multi def force_quotation_send(self): for order in self: email_act = order.action_quotation_send() if email_act and email_act.get('context'): email_ctx = email_act['context'] email_ctx.update(default_email_from=order.company_id.email) order.with_context(email_ctx).message_post_with_template( email_ctx.get('default_template_id')) return True @api.multi def action_done(self): self.write({'state': 'done'}) @api.model def _prepare_procurement_group(self): return {'name': self.name} @api.multi def action_confirm(self): for order in self: order.state = 'sale' if self.env.context.get('send_email'): self.force_quotation_send() order.order_line._action_procurement_create() if not order.project_id: for line in order.order_line: if line.product_id.invoice_policy == 'cost': order._create_analytic_account() break if self.env['ir.values'].get_default('sale.config.settings', 'auto_done_setting'): self.action_done() @api.multi def _create_analytic_account(self, prefix=None): for order in self: name = order.name if prefix: name = prefix + ": " + order.name analytic = self.env['account.analytic.account'].create({ 'name': name, 'code': order.client_order_ref, 'company_id': order.company_id.id, 'partner_id': order.partner_id.id }) order.project_id = analytic
class AccountFinancialReportLine(models.Model): _name = "account.financial.html.report.line" _description = "Account Report Line" _order = "sequence" name = fields.Char('Section Name', translate=True) code = fields.Char('Code') financial_report_id = fields.Many2one('account.financial.html.report', 'Financial Report') parent_id = fields.Many2one('account.financial.html.report.line', string='Parent') children_ids = fields.One2many('account.financial.html.report.line', 'parent_id', string='Children') sequence = fields.Integer() domain = fields.Char(default=None) formulas = fields.Char() groupby = fields.Char("Group by", default=False) figure_type = fields.Selection([('float', 'Float'), ('percents', 'Percents'), ('no_unit', 'No Unit')], 'Type', default='float', required=True) green_on_positive = fields.Boolean('Is growth good when positive', default=True) level = fields.Integer(required=True) special_date_changer = fields.Selection( [('from_beginning', 'From the beginning'), ('to_beginning_of_period', 'At the beginning of the period'), ('normal', 'Use given dates'), ('strict_range', 'Force given dates for all accounts and account types')], default='normal') show_domain = fields.Selection([('always', 'Always'), ('never', 'Never'), ('foldable', 'Foldable')], default='foldable') hide_if_zero = fields.Boolean(default=False) action_id = fields.Many2one('ir.actions.actions') @api.one @api.constrains('groupby') def _check_same_journal(self): if self.groupby and self.groupby not in self.env[ 'account.move.line']._columns: raise ValidationError("Groupby should be a journal item field") def _get_sum(self, field_names=None): ''' Returns the sum of the amls in the domain ''' if not field_names: field_names = ['debit', 'credit', 'balance', 'amount_residual'] res = dict((fn, 0.0) for fn in field_names) if self.domain: amls = self.env['account.move.line'].search(safe_eval(self.domain)) compute = amls._compute_fields(field_names) for aml in amls: if compute.get(aml.id): for field in field_names: res[field] += compute[aml.id][field] return res @api.one def get_balance(self, linesDict, field_names=None): if not field_names: field_names = ['debit', 'credit', 'balance'] res = dict((fn, 0.0) for fn in field_names) c = FormulaContext(self.env['account.financial.html.report.line'], linesDict, self) if self.formulas: for f in self.formulas.split(';'): [field, formula] = f.split('=') field = field.strip() if field in field_names: try: res[field] = safe_eval(formula, c, nocopy=True) except ValueError as err: if 'division by zero' in err.args[0]: res[field] = 0 else: raise err return res def _format(self, value): if self.env.context.get('no_format'): return value if self.figure_type == 'float': currency_id = self.env.user.company_id.currency_id if currency_id.is_zero(value): # don't print -0.0 in reports value = abs(value) return formatLang(self.env, value, currency_obj=currency_id) if self.figure_type == 'percents': return str(round(value * 100, 1)) + '%' return round(value, 1) def _get_gb_name(self, gb_id): if self.groupby == 'account_id': return self.env['account.account'].browse(gb_id).name_get()[0][1] if self.groupby == 'user_type_id': return self.env['account.account.type'].browse(gb_id).name if self.groupby == 'partner_id': return self.env['res.partner'].browse(gb_id).name return gb_id def _build_cmp(self, balance, comp): if comp != 0: res = round((balance - comp) / comp * 100, 1) if (res > 0) != self.green_on_positive: return (str(res) + '%', 'color: red;') else: return (str(res) + '%', 'color: green;') else: return 'n/a' def _split_formulas(self): result = {} if self.formulas: for f in self.formulas.split(';'): [column, formula] = f.split('=') column = column.strip() result.update({column: formula}) return result def _eval_formula(self, financial_report, debit_credit, context, currency_table, linesDict): debit_credit = debit_credit and financial_report.debit_credit formulas = self._split_formulas() if self.code and self.code in linesDict: res = linesDict[self.code] else: res = FormulaLine(self, linesDict=linesDict) vals = {} vals['balance'] = res.balance if debit_credit: vals['credit'] = res.credit vals['debit'] = res.debit results = {} if self.domain and self.groupby and self.show_domain != 'never': aml_obj = self.env['account.move.line'] tables, where_clause, where_params = aml_obj._query_get( domain=self.domain) params = [] if currency_table.keys(): groupby = self.groupby or 'id' if groupby not in self.env['account.move.line']._columns: raise ValueError( 'Groupby should be a field from account.move.line') select = ',COALESCE(SUM(CASE ' for currency_id in currency_table.keys(): params += [currency_id, currency_table[currency_id]] select += 'WHEN \"account_move_line\".company_currency_id = %s THEN (\"account_move_line\".debit-\"account_move_line\".credit) * %s ' select += 'ELSE \"account_move_line\".debit-\"account_move_line\".credit END), 0),SUM(CASE ' for currency_id in currency_table.keys(): params += [currency_id, currency_table[currency_id]] select += 'WHEN \"account_move_line\".company_currency_id = %s THEN \"account_move_line\".amount_residual * %s ' select += 'ELSE \"account_move_line\".amount_residual END)' if financial_report.debit_credit and debit_credit: select += ',SUM(CASE ' for currency_id in currency_table.keys(): params += [currency_id, currency_table[currency_id]] select += 'WHEN \"account_move_line\".company_currency_id = %s THEN \"account_move_line\".debit * %s ' select += 'ELSE \"account_move_line\".debit END),SUM(CASE ' for currency_id in currency_table.keys(): params += [currency_id, currency_table[currency_id]] select += 'WHEN \"account_move_line\".company_currency_id = %s THEN \"account_move_line\".credit * %s ' select += 'ELSE \"account_move_line\".credit END)' if self.env.context.get('cash_basis'): select = select.replace('debit', 'debit_cash_basis').replace( 'credit', 'credit_cash_basis') sql = "SELECT \"account_move_line\"." + groupby + select + " FROM " + tables + " WHERE " + where_clause + " GROUP BY \"account_move_line\".company_currency_id,\"account_move_line\"." + groupby else: groupby = self.groupby or 'id' select = ',COALESCE(SUM(\"account_move_line\".debit-\"account_move_line\".credit), 0),SUM(\"account_move_line\".amount_residual)' if financial_report.debit_credit and debit_credit: select += ',SUM(\"account_move_line\".debit),SUM(\"account_move_line\".credit)' if self.env.context.get('cash_basis'): select = select.replace('debit', 'debit_cash_basis').replace( 'credit', 'credit_cash_basis') sql = "SELECT \"account_move_line\"." + groupby + select + " FROM " + tables + " WHERE " + where_clause + " GROUP BY \"account_move_line\".company_currency_id,\"account_move_line\"." + groupby params += where_params self.env.cr.execute(sql, params) results = self.env.cr.fetchall() if financial_report.debit_credit and debit_credit: results = dict([(k[0], { 'balance': k[1], 'amount_residual': k[2], 'debit': k[3], 'credit': k[4] }) for k in results]) else: results = dict([(k[0], { 'balance': k[1], 'amount_residual': k[2] }) for k in results]) c = FormulaContext(self.env['account.financial.html.report.line'], linesDict) if formulas: for key in results: c['sum'] = FormulaLine(results[key], type='not_computed') for col, formula in formulas.items(): if col in results[key]: results[key][col] = safe_eval(formula, c, nocopy=True) to_del = [] for key in results: if self.env.user.company_id.currency_id.is_zero( results[key]['balance']): to_del.append(key) for key in to_del: del results[key] results.update({'line': vals}) return results def _put_columns_together(self, data, domain_ids): res = dict((domain_id, []) for domain_id in domain_ids) for period in data: debit_credit = False if 'debit' in period['line']: debit_credit = True for domain_id in domain_ids: if debit_credit: res[domain_id].append( period.get(domain_id, {'debit': 0})['debit']) res[domain_id].append( period.get(domain_id, {'credit': 0})['credit']) res[domain_id].append( period.get(domain_id, {'balance': 0})['balance']) return res def _divide_line(self, line): line1 = { 'id': line['id'], 'name': line['name'], 'type': 'line', 'level': line['level'], 'footnotes': line['footnotes'], 'columns': [''] * len(line['columns']), 'unfoldable': line['unfoldable'], 'unfolded': line['unfolded'], } line2 = { 'id': line['id'], 'name': _('Total') + ' ' + line['name'], 'type': 'total', 'level': line['level'] + 1, 'footnotes': self.env.context['context']._get_footnotes('total', line['id']), 'columns': line['columns'], 'unfoldable': False, } return [line1, line2] @api.multi def get_lines(self, financial_report, context, currency_table, linesDicts): final_result_table = [] comparison_table = context.get_periods() # build comparison table for line in self: res = [] debit_credit = len(comparison_table) == 1 domain_ids = {'line'} k = 0 for period in comparison_table: period_from = period[0] period_to = period[1] strict_range = False if line.special_date_changer == 'from_beginning': period_from = False if line.special_date_changer == 'to_beginning_of_period': date_tmp = datetime.strptime( period[0], "%Y-%m-%d") - relativedelta(days=1) period_to = date_tmp.strftime('%Y-%m-%d') period_from = False if line.special_date_changer == 'strict_range': strict_range = True r = line.with_context(date_from=period_from, date_to=period_to, strict_range=strict_range)._eval_formula( financial_report, debit_credit, context, currency_table, linesDicts[k]) debit_credit = False res.append(r) domain_ids.update(set(r.keys())) k += 1 res = self._put_columns_together(res, domain_ids) if line.hide_if_zero and sum( [k == 0 and [True] or [] for k in res['line']], []): continue # Post-processing ; creating line dictionnary, building comparison, computing total for extended, formatting vals = { 'id': line.id, 'name': line.name, 'type': 'line', 'level': line.level, 'footnotes': context._get_footnotes('line', line.id), 'columns': res['line'], 'unfoldable': len(domain_ids) > 1 and line.show_domain != 'always', 'unfolded': line in context.unfolded_lines or line.show_domain == 'always', } if line.action_id: vals['action_id'] = line.action_id.id domain_ids.remove('line') lines = [vals] groupby = line.groupby or 'aml' if line in context.unfolded_lines or line.show_domain == 'always': if line.groupby == 'partner_id' or line.groupby == 'account_id': domain_ids = sorted(list(domain_ids), key=lambda k: line._get_gb_name(k)) for domain_id in domain_ids: name = line._get_gb_name(domain_id) vals = { 'id': domain_id, 'name': name and len(name) >= 45 and name[0:40] + '...' or name, 'level': 1, 'type': groupby, 'footnotes': context._get_footnotes(groupby, domain_id), 'columns': res[domain_id], } lines.append(vals) if domain_ids: lines.append({ 'id': line.id, 'name': _('Total') + ' ' + line.name, 'type': 'o_account_reports_domain_total', 'level': 1, 'footnotes': context._get_footnotes( 'o_account_reports_domain_total', line.id), 'columns': list(lines[0]['columns']), }) for vals in lines: if financial_report.report_type == 'date_range_extended': vals['columns'].append(sum(vals['columns'])) if len(comparison_table) == 2: vals['columns'].append( line._build_cmp(vals['columns'][0], vals['columns'][1])) for i in [0, 1]: vals['columns'][i] = line._format(vals['columns'][i]) else: vals['columns'] = map(line._format, vals['columns']) if not line.formulas: vals['columns'] = ['' for k in vals['columns']] if len(lines) == 1: new_lines = line.children_ids.get_lines( financial_report, context, currency_table, linesDicts) if new_lines and line.level > 0 and line.formulas: divided_lines = self._divide_line(lines[0]) result = [divided_lines[0] ] + new_lines + [divided_lines[1]] else: result = [] if line.level > 0: result += lines result += new_lines if line.level <= 0: result += lines else: result = lines final_result_table += result return final_result_table
class ComunicazioneLiquidazione(models.Model): _name = 'comunicazione.liquidazione' _description = 'Comunicazione Liquidazione IVA' @api.model def _default_company(self): company_id = self._context.get('company_id', self.env.user.company_id.id) return company_id @api.constrains('identificativo') def _check_identificativo(self): domain = [('identificativo', '=', self.identificativo)] dichiarazioni = self.search(domain) if len(dichiarazioni) > 1: raise ValidationError( _("Dichiarazione già esiste con identificativo {}").format( self.identificativo)) @api.multi def _compute_name(self): for dich in self: name = "" for quadro in dich.quadri_vp_ids: if not name: period_type = '' if quadro.period_type == 'month': period_type = _('month') else: period_type = _('quarter') name += '{} {}'.format(str(dich.year), period_type) if quadro.period_type == 'month': name += ', {}'.format(str(quadro.month)) else: name += ', {}'.format(str(quadro.quarter)) dich.name = name def _get_identificativo(self): dichiarazioni = self.search([]) if dichiarazioni: return len(dichiarazioni) + 1 else: return 1 company_id = fields.Many2one('res.company', string='Company', required=True, default=_default_company) identificativo = fields.Integer(string='Identificativo', default=_get_identificativo) name = fields.Char(string='Name', compute="_compute_name") year = fields.Integer(string='Year', required=True, size=4) last_month = fields.Integer(string='Last month') liquidazione_del_gruppo = fields.Boolean(string='Liquidazione del gruppo') taxpayer_vat = fields.Char(string='Vat', required=True) controller_vat = fields.Char(string='Controller Vat') taxpayer_fiscalcode = fields.Char(string='Fiscalcode') declarant_different = fields.Boolean( string='Declarant different from taxpayer') declarant_fiscalcode = fields.Char(string='Fiscalcode') declarant_fiscalcode_company = fields.Char(string='Fiscalcode company') codice_carica_id = fields.Many2one('codice.carica', string='Codice carica') declarant_sign = fields.Boolean(string='Declarant sign', default=True) delegate_fiscalcode = fields.Char(string='Fiscalcode') delegate_commitment = fields.Selection( [('1', 'Comunicazione è stata predisposta dal contribuente '), ('2', 'Comunicazione è stata predisposta da chi effettua l’invio')], string='Commitment') delegate_sign = fields.Boolean(string='Delegate sign') date_commitment = fields.Date(string='Date commitment') quadri_vp_ids = fields.One2many('comunicazione.liquidazione.vp', 'comunicazione_id', string="Quadri VP") iva_da_versare = fields.Float(string='IVA da versare', readonly=True) iva_a_credito = fields.Float(string='IVA a credito', readonly=True) @api.model def create(self, vals): comunicazione = super(ComunicazioneLiquidazione, self).create(vals) comunicazione._validate() return comunicazione @api.multi def write(self, vals): super(ComunicazioneLiquidazione, self).write(vals) for comunicazione in self: comunicazione._validate() return True @api.onchange('company_id') def onchange_company_id(self): if self.company_id: if self.company_id.partner_id.vat: self.taxpayer_vat = self.company_id.partner_id.vat[2:] else: self.taxpayer_vat = '' self.taxpayer_fiscalcode = \ self.company_id.partner_id.fiscalcode def get_export_xml(self): self._validate() x1_Fornitura = self._export_xml_get_fornitura() x1_1_Intestazione = self._export_xml_get_intestazione() attrs = {'identificativo': str(self.identificativo).zfill(5)} x1_2_Comunicazione = etree.Element(etree.QName(NS_IV, "Comunicazione"), attrs) x1_2_1_Frontespizio = self._export_xml_get_frontespizio() x1_2_Comunicazione.append(x1_2_1_Frontespizio) x1_2_2_DatiContabili = etree.Element( etree.QName(NS_IV, "DatiContabili")) nr_modulo = 0 for quadro in self.quadri_vp_ids: nr_modulo += 1 modulo = self.with_context(nr_modulo=nr_modulo)\ ._export_xml_get_dati_modulo(quadro) x1_2_2_DatiContabili.append(modulo) x1_2_Comunicazione.append(x1_2_2_DatiContabili) # Composizione struttura xml con le varie sezioni generate x1_Fornitura.append(x1_1_Intestazione) x1_Fornitura.append(x1_2_Comunicazione) xml_string = etree.tostring(x1_Fornitura, encoding='utf8', method='xml', pretty_print=True) return xml_string def _validate(self): """ Controllo congruità dati della comunicazione """ # Anno obbligatorio if not self.year: raise ValidationError(_("Year required")) # Codice Fiscale if not self.taxpayer_fiscalcode \ or len(self.taxpayer_fiscalcode) not in [11, 16]: raise ValidationError( _("Taxpayer Fiscalcode is required. It's accepted codes \ with lenght 11 or 16 chars")) # Codice Fiscale dichiarante Obbligatorio se il codice fiscale # del contribuente è di 11 caratteri if self.taxpayer_fiscalcode and len(self.taxpayer_fiscalcode) == 11\ and not self.declarant_fiscalcode: raise ValidationError( _("Declarant Fiscalcode is required. You can enable the \ section with different declarant option")) # LiquidazioneGruppo: elemento opzionale, di tipo DatoCB_Type. # Se presente non deve essere presente l’elemento PIVAControllante. # Non può essere presente se l’elemento CodiceFiscale è lungo 16 # caratteri. if self.liquidazione_del_gruppo: if self.controller_vat: raise ValidationError( _("Per liquidazione del gruppo, partita iva controllante\ deve essere vuota")) if len(self.taxpayer_fiscalcode) == 16: raise ValidationError( _("Liquidazione del gruppo non valida, visto il codice\ fiscale di 16 caratteri")) # CodiceCaricaDichiarante if self.declarant_fiscalcode: if not self.codice_carica_id: raise ValidationError( _("Indicare il codice carica del dichiarante")) # CodiceFiscaleSocieta: # Obbligatori per codice carica 9 if self.codice_carica_id and self.codice_carica_id.code == '9': if not self.declarant_fiscalcode_company: raise ValidationError( _("Visto il codice carica, occorre indicare il codice \ fiscale della socità dichiarante")) # ImpegnoPresentazione:: if self.delegate_fiscalcode: if not self.delegate_commitment: raise ValidationError( _("Visto il codice fiscale dell'intermediario, occorre \ indicare il codice l'impegno")) if not self.date_commitment: raise ValidationError( _("Visto il codice fiscale dell'intermediario, occorre \ indicare la data dell'impegno")) # ImpegnoPresentazione:: if self.delegate_fiscalcode and not self.delegate_sign: raise ValidationError( _("In presenza dell'incaricato nella sezione impegno \ alla presentazione telematica, è necessario vistare \ l'opzione firma dell'incaricato")) return True def _export_xml_get_fornitura(self): x1_Fornitura = etree.Element(etree.QName(NS_IV, "Fornitura"), nsmap=NS_MAP) return x1_Fornitura def _export_xml_validate(self): return True def _export_xml_get_intestazione(self): x1_1_Intestazione = etree.Element(etree.QName(NS_IV, "Intestazione")) # Codice Fornitura x1_1_1_CodiceFornitura = etree.SubElement( x1_1_Intestazione, etree.QName(NS_IV, "CodiceFornitura")) code = str(self.year)[-2:] x1_1_1_CodiceFornitura.text = unicode('IVP{}'.format(code)) # Codice Fiscale Dichiarante if self.declarant_fiscalcode: x1_1_2_CodiceFiscaleDichiarante = etree.SubElement( x1_1_Intestazione, etree.QName(NS_IV, "CodiceFiscaleDichiarante")) x1_1_2_CodiceFiscaleDichiarante.text = unicode( self.declarant_fiscalcode) # Codice Carica if self.codice_carica_id: x1_1_3_CodiceCarica = etree.SubElement( x1_1_Intestazione, etree.QName(NS_IV, "CodiceCarica")) x1_1_3_CodiceCarica.text = unicode(self.codice_carica_id.code) return x1_1_Intestazione def _export_xml_get_frontespizio(self): x1_2_1_Frontespizio = etree.Element(etree.QName(NS_IV, "Frontespizio")) # Codice Fiscale x1_2_1_1_CodiceFiscale = etree.SubElement( x1_2_1_Frontespizio, etree.QName(NS_IV, "CodiceFiscale")) x1_2_1_1_CodiceFiscale.text = unicode(self.taxpayer_fiscalcode) \ if self.taxpayer_fiscalcode else '' # Anno Imposta x1_2_1_2_AnnoImposta = etree.SubElement( x1_2_1_Frontespizio, etree.QName(NS_IV, "AnnoImposta")) x1_2_1_2_AnnoImposta.text = str(self.year) # Partita IVA x1_2_1_3_PartitaIVA = etree.SubElement( x1_2_1_Frontespizio, etree.QName(NS_IV, "PartitaIVA")) x1_2_1_3_PartitaIVA.text = self.taxpayer_vat # PIVA Controllante if self.controller_vat: x1_2_1_4_PIVAControllante = etree.SubElement( x1_2_1_Frontespizio, etree.QName(NS_IV, "PIVAControllante")) x1_2_1_4_PIVAControllante.text = self.controller_vat # Ultimo Mese if self.last_month: x1_2_1_5_UltimoMese = etree.SubElement( x1_2_1_Frontespizio, etree.QName(NS_IV, "UltimoMese")) x1_2_1_5_UltimoMese.text = self.last_month # Liquidazione Gruppo x1_2_1_6_LiquidazioneGruppo = etree.SubElement( x1_2_1_Frontespizio, etree.QName(NS_IV, "LiquidazioneGruppo")) x1_2_1_6_LiquidazioneGruppo.text = \ '1' if self.liquidazione_del_gruppo else '0' # CF Dichiarante if self.declarant_fiscalcode: x1_2_1_7_CFDichiarante = etree.SubElement( x1_2_1_Frontespizio, etree.QName(NS_IV, "CFDichiarante")) x1_2_1_7_CFDichiarante.text = self.declarant_fiscalcode # CodiceCaricaDichiarante if self.codice_carica_id: x1_2_1_8_CodiceCaricaDichiarante = etree.SubElement( x1_2_1_Frontespizio, etree.QName(NS_IV, "CodiceCaricaDichiarante")) x1_2_1_8_CodiceCaricaDichiarante.text = self.codice_carica_id.code # CodiceFiscaleSocieta if self.declarant_fiscalcode_company: x1_2_1_9_CodiceFiscaleSocieta = etree.SubElement( x1_2_1_Frontespizio, etree.QName(NS_IV, "CodiceFiscaleSocieta")) x1_2_1_9_CodiceFiscaleSocieta.text =\ self.declarant_fiscalcode_company.code # FirmaDichiarazione x1_2_1_10_FirmaDichiarazione = etree.SubElement( x1_2_1_Frontespizio, etree.QName(NS_IV, "FirmaDichiarazione")) x1_2_1_10_FirmaDichiarazione.text = '1' if self.declarant_sign else '0' # CFIntermediario if self.delegate_fiscalcode: x1_2_1_11_CFIntermediario = etree.SubElement( x1_2_1_Frontespizio, etree.QName(NS_IV, "CFIntermediario")) x1_2_1_11_CFIntermediario.text = self.delegate_fiscalcode # ImpegnoPresentazione if self.delegate_commitment: x1_2_1_12_ImpegnoPresentazione = etree.SubElement( x1_2_1_Frontespizio, etree.QName(NS_IV, "ImpegnoPresentazione")) x1_2_1_12_ImpegnoPresentazione.text = self.delegate_commitment # DataImpegno if self.date_commitment: x1_2_1_13_DataImpegno = etree.SubElement( x1_2_1_Frontespizio, etree.QName(NS_IV, "DataImpegno")) x1_2_1_13_DataImpegno.text = datetime.strptime( self.date_commitment, "%Y-%m-%d").strftime('%d%m%Y') # FirmaIntermediario if self.delegate_fiscalcode: x1_2_1_14_FirmaIntermediario = etree.SubElement( x1_2_1_Frontespizio, etree.QName(NS_IV, "FirmaIntermediario")) x1_2_1_14_FirmaIntermediario.text =\ '1' if self.delegate_sign else '0' return x1_2_1_Frontespizio def _export_xml_get_dati_modulo(self, quadro): # 1.2.2.1 Modulo xModulo = etree.Element(etree.QName(NS_IV, "Modulo")) # Numero Modulo NumeroModulo = etree.SubElement(xModulo, etree.QName(NS_IV, "NumeroModulo")) NumeroModulo.text = str(self._context.get('nr_modulo', 1)) if quadro.period_type == 'month': # 1.2.2.1.1 Mese Mese = etree.SubElement(xModulo, etree.QName(NS_IV, "Mese")) Mese.text = str(quadro.month) else: # 1.2.2.1.2 Trimestre Trimestre = etree.SubElement(xModulo, etree.QName(NS_IV, "Trimestre")) Trimestre.text = str(quadro.quarter) # Da escludere per liquidazione del gruppo if not self.liquidazione_del_gruppo: # 1.2.2.1.3 Subfornitura if quadro.subcontracting: Subfornitura = etree.SubElement( xModulo, etree.QName(NS_IV, "Subfornitura")) Subfornitura.text = '1' if quadro.subcontracting \ else '0' # 1.2.2.1.4 EventiEccezionali if quadro.exceptional_events: EventiEccezionali = etree.SubElement( xModulo, etree.QName(NS_IV, "EventiEccezionali")) EventiEccezionali.text = quadro.exceptional_events # 1.2.2.1.5 TotaleOperazioniAttive TotaleOperazioniAttive = etree.SubElement( xModulo, etree.QName(NS_IV, "TotaleOperazioniAttive")) TotaleOperazioniAttive.text = "{:.2f}"\ .format(quadro.imponibile_operazioni_attive).replace('.', ',') # 1.2.2.1.6 TotaleOperazioniPassive TotaleOperazioniPassive = etree.SubElement( xModulo, etree.QName(NS_IV, "TotaleOperazioniPassive")) TotaleOperazioniPassive.text = "{:.2f}"\ .format(quadro.imponibile_operazioni_passive).replace('.', ',') # 1.2.2.1.7 IvaEsigibile IvaEsigibile = etree.SubElement(xModulo, etree.QName(NS_IV, "IvaEsigibile")) IvaEsigibile.text = "{:.2f}".format(quadro.iva_esigibile)\ .replace('.', ',') # 1.2.2.1.8 IvaDetratta IvaDetratta = etree.SubElement(xModulo, etree.QName(NS_IV, "IvaDetratta")) IvaDetratta.text = "{:.2f}".format(quadro.iva_detratta)\ .replace('.', ',') # 1.2.2.1.9 IvaDovuta if quadro.iva_dovuta_debito: IvaDovuta = etree.SubElement(xModulo, etree.QName(NS_IV, "IvaDovuta")) IvaDovuta.text = "{:.2f}".format(quadro.iva_dovuta_debito)\ .replace('.', ',') # 1.2.2.1.10 IvaCredito if quadro.iva_dovuta_credito: IvaCredito = etree.SubElement(xModulo, etree.QName(NS_IV, "IvaCredito")) IvaCredito.text = "{:.2f}".format(quadro.iva_dovuta_credito)\ .replace('.', ',') # 1.2.2.1.11 DebitoPrecedente DebitoPrecedente = etree.SubElement( xModulo, etree.QName(NS_IV, "DebitoPrecedente")) DebitoPrecedente.text = "{:.2f}".format( quadro.debito_periodo_precedente).replace('.', ',') # 1.2.2.1.12 CreditoPeriodoPrecedente CreditoPeriodoPrecedente = etree.SubElement( xModulo, etree.QName(NS_IV, "CreditoPeriodoPrecedente")) CreditoPeriodoPrecedente.text = "{:.2f}".format( quadro.credito_periodo_precedente).replace('.', ',') # 1.2.2.1.13 CreditoAnnoPrecedente CreditoAnnoPrecedente = etree.SubElement( xModulo, etree.QName(NS_IV, "CreditoAnnoPrecedente")) CreditoAnnoPrecedente.text = "{:.2f}".format( quadro.credito_anno_precedente).replace('.', ',') # 1.2.2.1.14 VersamentiAutoUE VersamentiAutoUE = etree.SubElement( xModulo, etree.QName(NS_IV, "VersamentiAutoUE")) VersamentiAutoUE.text = "{:.2f}".format( quadro.versamento_auto_UE).replace('.', ',') # 1.2.2.1.15 CreditiImposta CreditiImposta = etree.SubElement(xModulo, etree.QName(NS_IV, "CreditiImposta")) CreditiImposta.text = "{:.2f}".format(quadro.crediti_imposta).replace( '.', ',') # 1.2.2.1.16 InteressiDovuti InteressiDovuti = etree.SubElement( xModulo, etree.QName(NS_IV, "InteressiDovuti")) InteressiDovuti.text = "{:.2f}".format( quadro.interessi_dovuti).replace('.', ',') # 1.2.2.1.17 Acconto Acconto = etree.SubElement(xModulo, etree.QName(NS_IV, "Acconto")) Acconto.text = "{:.2f}".format(quadro.accounto_dovuto).replace( '.', ',') # 1.2.2.1.18 ImportoDaVersare ImportoDaVersare = etree.SubElement( xModulo, etree.QName(NS_IV, "ImportoDaVersare")) ImportoDaVersare.text = "{:.2f}".format(quadro.iva_da_versare).replace( '.', ',') # 1.2.2.1.19 ImportoACredito ImportoACredito = etree.SubElement( xModulo, etree.QName(NS_IV, "ImportoACredito")) ImportoACredito.text = "{:.2f}".format(quadro.iva_a_credito).replace( '.', ',') return xModulo
class EdiConfigSystem(models.Model): _name = 'edi.config.system' name = fields.Char(string="Name", required=True) supplier_id = fields.Many2one(comodel_name="res.partner", string="EDI supplier", domain=[('supplier', '=', True), ('is_edi', '=', True)], required=True) parent_supplier_id = fields.Many2one(comodel_name="res.partner", string="EDI Parent supplier", domain=[('supplier', '=', True), ('is_edi', '=', True)]) ftp_host = fields.Char(string="FTP Server Host", default='xxx.xxx.xxx.xxx', required=True) ftp_port = fields.Char(string="FTP Server Port", default='21', required=True) ftp_login = fields.Char(string="FTP Login", required=True) ftp_password = fields.Char(string="FTP Password", required=True) csv_relative_in_path = fields.Char( string="Relative path for IN interfaces", default='/', required=True) csv_relative_out_path = fields.Char( string="Relative path for OUT interfaces", default='/', required=True) po_text_file_pattern = fields.Char(string="Purchase order File pattern", required=True) do_text_file_pattern = fields.Char(string="Delivery order File pattern") pricing_text_file_pattern = fields.Char(string="Pricing File pattern") customer_code = fields.Char(string="Customer code", required=True) constant_file_start = fields.Char(string="Constant file start", required=True) constant_file_end = fields.Char(string="Constant file end", required=True) vrp_code = fields.Char(string="VRP Code", required=True) mapping_ids = fields.One2many(comodel_name="edi.mapping.lines", inverse_name="config_id") price_mapping_ids = fields.One2many(comodel_name="edi.price.mapping", inverse_name="price_config_id") ble_mapping_ids = fields.One2many(comodel_name="edi.ble.mapping", inverse_name="ble_config_id") delivery_sign = fields.Char(string="Delivery sign") days = fields.Integer(string="Frequency check (days)") header_code = fields.Char(string="Header code") lines_code = fields.Char(string="Lines code") @api.one @api.constrains('ftp_port') def _check_ftp_port(self): if not self.ftp_port.isdigit(): raise ValidationError(_("FTP port must be numeric!")) @api.model def ftp_connection_open(self, edi_system): """Return a new FTP connection with found parameters.""" _logger.info( "Trying to connect to ftp://%s@%s:%s" % (edi_system.ftp_login, edi_system.ftp_host, edi_system.ftp_port)) try: ftp = FTP() ftp.connect(edi_system.ftp_host) if edi_system.ftp_login: ftp.login(edi_system.ftp_login, edi_system.ftp_password) else: ftp.login() return ftp except Exception, e: raise ValidationError( _("Error when opening FTP connection:\n %s") % tools.ustr(e))
class is_cout_calcul(models.Model): _name='is.cout.calcul' _order='name desc' name = fields.Datetime('Date', required=True , readonly=True) user_id = fields.Many2one('res.users', 'Responsable', readonly=True) product_id = fields.Many2one('product.product', 'Article') segment_id = fields.Many2one('is.product.segment', 'Segment') is_category_id = fields.Many2one('is.category', 'Catégorie') is_gestionnaire_id = fields.Many2one('is.gestionnaire', 'Gestionnaire') multiniveaux = fields.Boolean('Calcul des coûts multi-niveaux') cout_actualise_ids = fields.One2many('is.cout.calcul.actualise', 'cout_calcul_id', u"Historique des côuts actualisés") niveau_ids = fields.One2many('is.cout.calcul.niveau' , 'cout_calcul_id', u"Niveau des articles dans la nomenclature") log_ids = fields.One2many('is.cout.calcul.log', 'cout_calcul_id', u"Logs") state = fields.Selection([('creation',u'Création'), ('prix_achat', u"Calcul des prix d'achat"),('termine', u"Terminé")], u"État", readonly=True, select=True) _defaults = { 'name': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), 'user_id': lambda self, cr, uid, c: uid, 'multiniveaux': True, 'state': 'creation', } detail_nomenclature=[] detail_gamme_ma=[] detail_gamme_mo=[] detail_gamme_ma_pk=[] detail_gamme_mo_pk=[] mem_couts={} @api.multi @api.multi def nomenclature(self, cout_calcul_obj, product, niveau, multiniveaux=True): cr = self._cr type_article=self.type_article(product) cout=self.creation_cout(cout_calcul_obj, product, type_article) if type_article!='A' and multiniveaux==True: if niveau>10: raise Warning(u"Trop de niveaux (>10) dans la nomenclature du "+product.is_code) SQL=""" select mbl.product_id, mbl.id, mbl.sequence, mb.id from mrp_bom mb inner join mrp_bom_line mbl on mbl.bom_id=mb.id inner join product_product pp on pp.product_tmpl_id=mb.product_tmpl_id where pp.id="""+str(product.id)+ """ order by mbl.sequence, mbl.id """ #TODO : Voir si ce filtre est necessaire : and (mb.is_sous_traitance='f' or mb.is_sous_traitance is null) cr.execute(SQL) result = cr.fetchall() niv=niveau+1 for row2 in result: composant=self.env['product.product'].browse(row2[0]) self.nomenclature(cout_calcul_obj, composant, niv) @api.multi def type_article(self, product): type_article="" for route in product.route_ids: if type_article=='F' and route.name=='Buy': type_article='ST' if type_article=='A' and route.name=='Manufacture': type_article='ST' if type_article=='' and route.name=='Manufacture': type_article='F' if type_article=='' and route.name=='Buy': type_article='A' return type_article @api.multi def creation_cout(self, cout_calcul_obj, product, type_article, niveau=0): product_id=product.id #_logger.info('creation_cout : len(mem_couts)='+str(len(self.mem_couts))) if product_id in self.mem_couts: action='trouvé' cout=self.mem_couts[product_id] else: cout_obj = self.env['is.cout'] couts=cout_obj.search([('name', '=', product_id)]) vals={ 'cout_calcul_id': cout_calcul_obj.id, 'type_article' : type_article, 'niveau' : niveau, } if len(couts): action='write' cout=couts[0] cout.write(vals) else: action='create' vals['name'] = product_id cout=cout_obj.create(vals) self.mem_couts[product_id]=cout return cout @api.multi def action_imprimer_couts(self): for obj in self: tmp=tempfile.mkdtemp() os.system('mkdir '+tmp) ct=1 nb=len(obj.cout_actualise_ids) for line in obj.cout_actualise_ids: couts=self.env['is.cout'].search([('name', '=', line.product_id.id)]) for cout in couts: path=tmp+"/"+str(ct)+".pdf" ct=ct+1 pdf = self.env['report'].get_pdf(cout, 'is_plastigray.report_is_cout') f = open(path,'wb') f.write(pdf) f.close() os.system('pdfjoin -o '+tmp+'/merged.pdf '+tmp+'/*.pdf') pdf = open(tmp+'/merged.pdf','rb').read() os.system('rm '+tmp+'/*.pdf') os.system('rmdir '+tmp) # ** Recherche si une pièce jointe est déja associèe *************** model=self._name name='Couts.pdf' attachment_obj = self.env['ir.attachment'] attachments = attachment_obj.search([('res_model','=',model),('res_id','=',obj.id),('name','=',name)]) # ****************************************************************** # ** Creation ou modification de la pièce jointe ******************* vals = { 'name': name, 'datas_fname': name, 'type': 'binary', 'res_model': model, 'res_id': obj.id, 'datas': pdf.encode('base64'), } attachment_id=False if attachments: for attachment in attachments: attachment.write(vals) attachment_id=attachment.id else: attachment = attachment_obj.create(vals) attachment_id=attachment.id return { 'type' : 'ir.actions.act_url', 'url': '/web/binary/saveas?model=ir.attachment&field=datas&id='+str(attachment_id)+'&filename_field=name', 'target': 'self', } #******************************************************************* @api.multi def _log(self,operation): _logger.info(operation) for obj in self: vals={ 'cout_calcul_id': obj.id, 'date' : datetime.datetime.now(), 'operation' : operation, } res=self.env['is.cout.calcul.log'].create(vals) # @api.multi # def action_calcul_prix_achat(self): # cr = self._cr # debut=datetime.datetime.now() # for obj in self: # #obj.log_ids.unlink() # self._log("## DEBUT Calcul des prix d'achat") # _logger.info('début unlink') # for row in obj.cout_actualise_ids: # row.unlink() # _logger.info('fin unlink') # calcul_actualise_obj = self.env['is.cout.calcul.actualise'] # _logger.info("début get_products") # products=self.get_products(obj) # _logger.info("fin get_products : nb="+str(len(products))) # #TODO : 0.07s par article (88s pour 1233 articles) # ct=1 # nb=len(products) # #print _now(debut), "## début boucle products : nb=",nb # _logger.info("début boucle products : nb="+str(nb)) # for product in products: # _logger.info(str(ct)+'/'+str(nb)+' : boucle products : '+product.is_code) # #print _now(debut), product,ct,'/',nb # ct+=1 # self.nomenclature(obj,product,0, obj.multiniveaux) # couts=self.env['is.cout'].search([('cout_calcul_id', '=', obj.id)]) # product_uom_obj = self.env['product.uom'] # #print _now(debut), "## fin boucle products" # _logger.info("fin boucle products") # ct=1 # nb=len(couts) # #print _now(debut), "## début boucle couts : nb=",nb # _logger.info("début boucle couts : nb="+str(nb)) # for cout in couts: # product=cout.name # _logger.info(str(ct)+'/'+str(nb)+' : boucle couts : '+product.is_code) # #print _now(debut), product.is_code,ct,'/',nb # ct+=1 # prix_tarif = 0 # prix_commande = 0 # prix_facture = 0 # prix_calcule = 0 # ecart_calcule_matiere = 0 # vals={ # 'cout_calcul_id': obj.id, # 'product_id': product.id, # } # res=calcul_actualise_obj.create(vals) # type_article=cout.type_article # if type_article!='F': # #** Recherche du fournisseur par défaut ******************** # seller=False # if len(product.seller_ids)>0: # seller=product.seller_ids[0] # pricelist=False # if seller: # partner=seller.name # pricelist=partner.property_product_pricelist_purchase # #*********************************************************** # #** Recherche du prix d'achat ****************************** # date=time.strftime('%Y-%m-%d') # Date du jour # if pricelist: # #Convertion du lot_mini de US vers UA # min_quantity = product_uom_obj._compute_qty(cout.name.uom_id.id, cout.name.lot_mini, cout.name.uom_po_id.id) # #TODO : Pour contourner un bug d'arrondi (le 31/01/2017) # min_quantity=min_quantity+0.00000000001 # #TODO en utilisant la fonction repr à la place de str, cela ne tronque pas les décimales # SQL=""" # select ppi.price_surcharge # from product_pricelist_version ppv inner join product_pricelist_item ppi on ppv.id=ppi.price_version_id # where ppv.pricelist_id="""+str(pricelist.id)+ """ # and min_quantity<="""+repr(min_quantity)+""" # and (ppv.date_start <= '"""+date+"""' or ppv.date_start is null) # and (ppv.date_end >= '"""+date+"""' or ppv.date_end is null) # and ppi.product_id="""+str(product.id)+ """ # and (ppi.date_start <= '"""+date+"""' or ppi.date_start is null) # and (ppi.date_end >= '"""+date+"""' or ppi.date_end is null) # order by ppi.sequence # limit 1 # """ # cr.execute(SQL) # result = cr.fetchall() # for row in result: # #coef=product.uom_po_id.factor_inv # coef=1 # if min_quantity: # coef=cout.name.lot_mini/min_quantity # prix_tarif=row[0]/coef # #*********************************************************** # #** Recherche prix dernière commande *********************** # SQL=""" # select pol.price_unit*pu.factor # from purchase_order_line pol inner join product_uom pu on pol.product_uom=pu.id # where pol.product_id="""+str(product.id)+ """ # and state in('confirmed','done') # order by pol.id desc limit 1 # """ # cr.execute(SQL) # result = cr.fetchall() # for row in result: # prix_commande=row[0] # #*********************************************************** # #** Recherche prix dernière facture ************************ # SQL=""" # select ail.price_unit*pu.factor # from account_invoice_line ail inner join product_uom pu on ail.uos_id=pu.id # inner join account_invoice ai on ail.invoice_id=ai.id # where ail.product_id="""+str(product.id)+ """ # and ai.state in('open','paid') and ai.type='in_invoice' # order by ail.id desc limit 1 # """ # cr.execute(SQL) # result = cr.fetchall() # for row in result: # prix_facture=row[0] # #*********************************************************** # if cout.prix_force: # prix_calcule=cout.prix_force # else: # if prix_facture: # prix_calcule=prix_facture # else: # if prix_commande: # prix_calcule=prix_commande # else: # if prix_tarif: # prix_calcule=prix_tarif # if type_article=='A': # if prix_calcule==0: # prix_calcule=cout.cout_act_matiere # ecart_calcule_matiere = prix_calcule - cout.cout_act_matiere # if type_article=='ST': # if prix_calcule==0: # prix_calcule=cout.cout_act_st # ecart_calcule_matiere = prix_calcule - cout.cout_act_st # if prix_tarif: # cout.prix_tarif=prix_tarif # cout.type_article = type_article # cout.prix_commande = prix_commande # cout.prix_facture = prix_facture # cout.prix_calcule = prix_calcule # cout.ecart_calcule_matiere = ecart_calcule_matiere # obj.state="prix_achat" # self._log("## FIN Calcul des prix d'achat"+_now(debut)) @api.multi def get_products(self,obj): cats=self.env['is.category']._calcul_cout() products={} if obj.product_id: products=self.env['product.product'].search([('id', '=', obj.product_id.id), ('is_category_id', 'in', cats)]) else: if obj.segment_id: products=self.env['product.product'].search([('segment_id', '=', obj.segment_id.id), ('is_category_id', 'in', cats)], limit=10000) else: if obj.is_category_id: products=self.env['product.product'].search([('is_category_id', '=', obj.is_category_id.id)], limit=10000) else: if obj.is_gestionnaire_id: products=self.env['product.product'].search([('is_gestionnaire_id', '=', obj.is_gestionnaire_id.id), ('is_category_id', 'in', cats)], limit=10000) else: products=self.env['product.product'].search([('is_category_id', 'in', cats)]) return products @api.multi def nomenclature_prix_revient(self, cout_calcul_obj, niveau, product, unite=False, quantite_unitaire=1, quantite_total=1, prix_calcule=0): cr = self._cr type_article=self.type_article(product) cout_mat = 0 cout_st = 0 msg_err='' if product.is_category_id.name!='80': if type_article=='A': cout_mat = prix_calcule if prix_calcule==0: msg_err=u'Err Coût Mat' if type_article=='ST': cout_st = prix_calcule if prix_calcule==0: msg_err=u'Err Coût ST' cout=self.creation_cout(cout_calcul_obj, product, type_article) self.detail_nomenclature.append({ 'product_id' : product.id, 'is_code' : product.is_code, 'composant' : '----------'[:niveau]+str(product.is_code), 'designation' : product.name, 'unite' : unite, 'quantite' : quantite_unitaire, 'cout_mat' : cout_mat, 'total_mat' : quantite_total*cout_mat, 'cout_st' : cout_st, 'total_st' : quantite_total*cout_st, 'msg_err' : msg_err, }) if type_article!='A': lot_mini=product.lot_mini if lot_mini==0: lot_mini=1 #** Recherche de la gamme ****************************************** SQL=""" select mb.routing_id from mrp_bom mb inner join product_product pp on pp.product_tmpl_id=mb.product_tmpl_id where pp.id="""+str(product.id)+ """ and (mb.is_sous_traitance='f' or mb.is_sous_traitance is null) order by mb.id """ cr.execute(SQL) result = cr.fetchall() for row2 in result: routing_id = row2[0] if routing_id: routing = self.env['mrp.routing'].browse(routing_id) for line in routing.workcenter_lines: cout_total=quantite_unitaire*line.workcenter_id.costs_hour*round(line.is_nb_secondes/3600,4) vals={ 'composant' : '----------'[:niveau]+product.is_code, 'sequence' : line.sequence, 'workcenter_id' : line.workcenter_id.id, 'quantite' : quantite_unitaire, 'cout_prepa' : line.workcenter_id.costs_hour, 'tps_prepa' : line.workcenter_id.time_start, 'cout_fab' : line.workcenter_id.costs_hour, 'tps_fab' : line.is_nb_secondes, 'cout_total' : cout_total, } if line.workcenter_id.resource_type=='material': self.detail_gamme_ma.append(vals) else: self.detail_gamme_mo.append(vals) #** Recherche de la gamme générique pour Cout Plasti-ka ************ SQL=""" select mb.is_gamme_generique_id from mrp_bom mb inner join product_product pp on pp.product_tmpl_id=mb.product_tmpl_id where pp.id="""+str(product.id)+ """ order by mb.id """ cr.execute(SQL) result = cr.fetchall() for row2 in result: routing_id = row2[0] if routing_id: routing = self.env['mrp.routing'].browse(routing_id) for line in routing.workcenter_lines: cout_total=quantite_unitaire*line.workcenter_id.is_cout_pk*round(line.is_nb_secondes/3600,4) vals={ 'composant' : '----------'[:niveau]+product.is_code, 'sequence' : line.sequence, 'workcenter_id' : line.workcenter_id.id, 'quantite' : quantite_unitaire, 'cout_prepa' : line.workcenter_id.is_cout_pk, 'tps_prepa' : line.workcenter_id.time_start, 'cout_fab' : line.workcenter_id.is_cout_pk, 'tps_fab' : line.is_nb_secondes, 'cout_total' : cout_total, } if line.workcenter_id.resource_type=='material': self.detail_gamme_ma_pk.append(vals) else: self.detail_gamme_mo_pk.append(vals) #******************************************************************* #** Composants de la nomenclature ********************************** SQL=""" select mbl.product_id, mbl.product_uom, mbl.product_qty, ic.prix_calcule from mrp_bom mb inner join mrp_bom_line mbl on mbl.bom_id=mb.id inner join product_product pp on pp.product_tmpl_id=mb.product_tmpl_id inner join is_cout ic on ic.name=mbl.product_id where pp.id="""+str(product.id)+ """ order by mbl.sequence, mbl.id """ # TODO : Filtre sur ce critère ? => and (mb.is_sous_traitance='f' or mb.is_sous_traitance is null) cr.execute(SQL) result = cr.fetchall() niv=niveau+1 for row2 in result: composant = self.env['product.product'].browse(row2[0]) unite = row2[1] qt_unitaire = row2[2] qt_total = qt_unitaire*quantite_total prix_calcule = row2[3] self.nomenclature_prix_revient(cout_calcul_obj, niv, composant, unite, qt_unitaire, qt_total, prix_calcule) #******************************************************************* @api.multi def _productid2cout(self,product_id): cout_obj = self.env['is.cout'] couts=cout_obj.search([('name', '=', product_id)]) return couts @api.multi def _get_couts(self): couts=[] for obj in self: for row in obj.cout_actualise_ids: product=row.product_id res=self._productid2cout(product.id) for r in res: couts.append(r) return couts @api.multi def _unlink_detail_cout(self,couts): """En regroupant la suppression de toutes les lignes, cela permet de gagner beaucoup de temps""" cr = self._cr if couts: ids = self._get_couts_ids(couts) if ids: ids=','.join(ids) SQL='' SQL+='DELETE FROM is_cout_nomenclature WHERE cout_id in('+ids+'); ' SQL+='DELETE FROM is_cout_gamme_ma WHERE cout_id in('+ids+'); ' SQL+='DELETE FROM is_cout_gamme_mo WHERE cout_id in('+ids+'); ' SQL+='DELETE FROM is_cout_gamme_ma_pk WHERE cout_id in('+ids+'); ' SQL+='DELETE FROM is_cout_gamme_mo_pk WHERE cout_id in('+ids+'); ' cr.execute(SQL) @api.multi def _get_couts_ids(self,couts): """Retourne la liste des id des couts à partir des couts""" ids=[] for cout in couts: ids.append(str(cout.id)) return ids @api.multi def _write_resultats(self): "Ecrit les résultats des calculs dans la page récapitulative" for obj in self: for row in obj.cout_actualise_ids: product=row.product_id couts=self._productid2cout(product.id) for cout in couts: vals={} vals['cout_act_matiere'] = cout.cout_act_matiere vals['cout_act_machine'] = cout.cout_act_machine vals['cout_act_mo'] = cout.cout_act_mo vals['cout_act_st'] = cout.cout_act_st vals['cout_act_total'] = cout.cout_act_total row.write(vals) @api.multi def action_calcul_prix_revient(self): #pr=cProfile.Profile() #pr.enable() for obj in self: self._log("## DEBUT Calcul des prix de revient") nb=len(obj.cout_actualise_ids) ct=0 couts = self._get_couts() self._unlink_detail_cout(couts) for cout in couts: product=cout.name ct=ct+1 _logger.info(str(ct)+'/'+str(nb)+' : '+str(product.is_code)) cout_act_matiere = 0 cout_act_st = 0 cout_act_condition = 0 cout_act_machine = 0 cout_act_machine_pk = 0 cout_act_mo = 0 cout_act_mo_pk = 0 cout_act_total = 0 if cout.type_article=='A': cout_act_matiere = cout.prix_calcule cout_act_st = 0 if cout.type_article=='ST': cout_act_matiere = 0 cout_act_st = 0 nb_err=0 if cout.type_article!='A': self.detail_nomenclature=[] self.detail_gamme_ma=[] self.detail_gamme_mo=[] self.detail_gamme_ma_pk=[] self.detail_gamme_mo_pk=[] self.nomenclature_prix_revient(obj, 0, product, False, 1, 1, cout.prix_calcule) for vals in self.detail_nomenclature: if vals['msg_err']!='': nb_err=nb_err+1 is_code=vals['is_code'] if is_code[:1]=="7": cout_act_condition=cout_act_condition+vals['total_mat'] del vals['is_code'] vals['cout_id']=cout.id cout_act_matiere = cout_act_matiere+vals['total_mat'] cout_act_st = cout_act_st+vals['total_st'] res=self.env['is.cout.nomenclature'].create(vals) vals={ 'cout_id' : cout.id, 'designation' : 'TOTAL : ', 'total_mat' : cout_act_matiere, 'total_st' : cout_act_st, } res=self.env['is.cout.nomenclature'].create(vals) vals={ 'cout_id' : cout.id, 'designation' : 'Conditionnement : ', 'total_mat' : cout_act_condition, } res=self.env['is.cout.nomenclature'].create(vals) for vals in self.detail_gamme_ma: vals['cout_id']=cout.id res=self.env['is.cout.gamme.ma'].create(vals) cout_act_machine = cout_act_machine+vals['cout_total'] for vals in self.detail_gamme_mo: vals['cout_id']=cout.id res=self.env['is.cout.gamme.mo'].create(vals) cout_act_mo = cout_act_mo+vals['cout_total'] for vals in self.detail_gamme_ma_pk: vals['cout_id']=cout.id res=self.env['is.cout.gamme.ma.pk'].create(vals) cout_act_machine_pk = cout_act_machine_pk+vals['cout_total'] for vals in self.detail_gamme_mo_pk: vals['cout_id']=cout.id res=self.env['is.cout.gamme.mo.pk'].create(vals) cout_act_mo_pk = cout_act_mo_pk+vals['cout_total'] vals={} #Client par défaut for row in product.is_client_ids: if row.client_defaut: vals['partner_id']=row.client_id.id vals['nb_err'] = nb_err if nb_err>0: cout_act_matiere=0 cout_act_total=cout_act_matiere+cout_act_machine+cout_act_mo+cout_act_st vals['cout_act_matiere'] = cout_act_matiere vals['cout_act_condition'] = cout_act_condition vals['cout_act_machine'] = cout_act_machine vals['cout_act_mo'] = cout_act_mo vals['cout_act_machine_pk'] = cout_act_machine_pk vals['cout_act_mo_pk'] = cout_act_mo_pk vals['cout_act_st'] = cout_act_st vals['cout_act_total'] = cout_act_total vals['is_category_id'] = product.is_category_id.id vals['is_gestionnaire_id'] = product.is_gestionnaire_id.id vals['is_mold_id'] = product.is_mold_id.id vals['is_mold_dossierf'] = product.is_mold_dossierf vals['uom_id'] = product.uom_id.id vals['lot_mini'] = product.lot_mini vals['cout_act_prix_vente'] = cout.prix_vente-cout.amortissement_moule-cout.surcout_pre_serie cout.write(vals) self._write_resultats() obj.state="termine" self._log("## FIN Calcul des prix de revient")
class LunchOrderLine(models.Model): _name = 'lunch.order.line' _description = 'lunch order line' name = fields.Char(related='product_id.name', string="Product Name", readonly=True) order_id = fields.Many2one('lunch.order', 'Order', ondelete='cascade', required=True) product_id = fields.Many2one('lunch.product', 'Product', required=True) category_id = fields.Many2one('lunch.product.category', string='Product Category', related='product_id.category_id', readonly=True, store=True) date = fields.Date(string='Date', related='order_id.date', readonly=True, store=True) supplier = fields.Many2one('res.partner', string='Vendor', related='product_id.supplier', readonly=True, store=True) user_id = fields.Many2one('res.users', string='User', related='order_id.user_id', readonly=True, store=True) note = fields.Text('Note') price = fields.Float(related='product_id.price', readonly=True, store=True, digits=dp.get_precision('Account')) state = fields.Selection([('new', 'New'), ('confirmed', 'Received'), ('ordered', 'Ordered'), ('cancelled', 'Cancelled')], 'Status', readonly=True, select=True, default='new') cashmove = fields.One2many('lunch.cashmove', 'order_id', 'Cash Move') currency_id = fields.Many2one('res.currency', related='order_id.currency_id') @api.one def order(self): """ The order_line is ordered to the vendor but isn't received yet """ if self.user_has_groups("lunch.group_lunch_manager"): self.state = 'ordered' else: raise AccessError(_("Only your lunch manager processes the orders.")) @api.one def confirm(self): """ confirm one or more order line, update order status and create new cashmove """ if self.user_has_groups("lunch.group_lunch_manager"): if self.state != 'confirmed': values = { 'user_id': self.user_id.id, 'amount': -self.price, 'description': self.product_id.name, 'order_id': self.id, 'state': 'order', 'date': self.date, } self.env['lunch.cashmove'].create(values) self.state = 'confirmed' else: raise AccessError(_("Only your lunch manager sets the orders as received.")) @api.one def cancel(self): """ cancel one or more order.line, update order status and unlink existing cashmoves """ if self.user_has_groups("lunch.group_lunch_manager"): self.state = 'cancelled' self.cashmove.unlink() else: raise AccessError(_("Only your lunch manager cancels the orders."))
class product_wizard(models.TransientModel): _name = 'product.batch_list' def compute_default_value(self): return self.env[self.env.context.get('active_model')].browse( self.env.context.get('active_id')) product_id = fields.Many2one(comodel_name='product.product', string='Product', default=compute_default_value) quant_ids = fields.One2many(comodel_name='product.batch_list_quant', string='Quants', inverse_name='batch_list_id') location_id = fields.Many2one( comodel_name='stock.location' ) # domain|context|ondelete="'set null', 'restrict', 'cascade'"|auto_join|delegate lot_id = fields.Many2one(comodel_name='stock.production.lot') @api.onchange('location_id') def onchange_location_id(self): # ~ raise Warning(self.env.context) domain = [('product_id', '=', self.product_id.id)] if self.location_id: domain.append(('location_id', 'child_of', self.location_id.id)) if self.lot_id: domain.append(('lot_id', '=', self.lot_id.id)) quants = self.env['stock.quant'].search(domain) quant_ids = [] if self.quant_ids: self.quant_ids.append((5, )) for quant in quants: quant_ids.append((0, 0, { 'name': quant.name, 'qty': quant.qty, 'lot_id': quant.lot_id.id, 'removal_date': quant.removal_date, 'quant_id': quant.id })) self.quant_ids = quant_ids self.product_id = self.env['product.product'].browse( self.env.context.get('active_id')) def choose_batch(self): for quant in self.quant_ids: quant.quant_id.sudo().write({ 'lot_id': quant.lot_id.id, 'qty': quant.qty }) @api.multi def empty_location_id(self, *args): location = self.env['stock.location'].search([('name', '=', 'Scrapped') ])[0] _logger.warn("stock_batch location %s" % location) move = self.env['stock.move'].create({ 'name': _('SCRAP:') + self.product_id.display_name, 'product_id': self.product_id.id, 'product_uom': self.product_id.uom_id.id, 'product_uom_qty': sum([q.qty for q in self.quant_ids.mapped('quant_id')]), 'date': fields.Datetime.now(), 'company_id': self.env.user.company_id.id, 'state': 'confirmed', 'location_id': self.location_id.id, 'location_dest_id': location.id, }) # ~ self.env['stock.quant'].quants_move([(q, q.qty) for q in self.quant_ids.mapped('quant_id')], None, location) move.action_done()
class LunchOrder(models.Model): """ A lunch order contains one or more lunch order line(s). It is associated to a user for a given date. When creating a lunch order, applicable lunch alerts are displayed. """ _name = 'lunch.order' _description = 'Lunch Order' _order = 'date desc' def _default_previous_order_ids(self): prev_order = self.env['lunch.order.line'].search([('user_id', '=', self.env.uid), ('product_id.active', '!=', False)], limit=20, order='id desc') # If we return return prev_order.ids, we will have duplicates (identical orders). # Therefore, this following part removes duplicates based on product_id and note. return { (order.product_id, order.note): order.id for order in prev_order }.values() user_id = fields.Many2one('res.users', 'User', required=True, readonly=True, states={'new': [('readonly', False)]}, default=lambda self: self.env.uid) date = fields.Date('Date', required=True, readonly=True, states={'new': [('readonly', False)]}, default=fields.Date.context_today) order_line_ids = fields.One2many('lunch.order.line', 'order_id', 'Products', ondelete="cascade", readonly=True, copy=True, states={'new': [('readonly', False)], False: [('readonly', False)]}) total = fields.Float(compute='_compute_total', string="Total", store=True) state = fields.Selection([('new', 'New'), ('confirmed', 'Received'), ('cancelled', 'Cancelled')], 'Status', readonly=True, index=True, copy=False, default='new', compute='_compute_order_state', store=True) alerts = fields.Text(compute='_compute_alerts_get', string="Alerts") previous_order_ids = fields.Many2many('lunch.order.line', compute='_compute_previous_order_ids', default=lambda self: self._default_previous_order_ids()) company_id = fields.Many2one('res.company', related='user_id.company_id', store=True) currency_id = fields.Many2one('res.currency', related='company_id.currency_id', readonly=True, store=True) cash_move_balance = fields.Monetary(compute='_compute_cash_move_balance', multi='cash_move_balance') balance_visible = fields.Boolean(compute='_compute_cash_move_balance', multi='cash_move_balance') @api.one @api.depends('order_line_ids') def _compute_total(self): """ get and sum the order lines' price """ self.total = sum( orderline.price for orderline in self.order_line_ids) @api.multi def name_get(self): return [(order.id, '%s %s' % (_('Lunch Order'), '#%d' % order.id)) for order in self] @api.depends('state') def _compute_alerts_get(self): """ get the alerts to display on the order form """ alert_msg = [alert.message for alert in self.env['lunch.alert'].search([]) if alert.display] if self.state == 'new': self.alerts = alert_msg and '\n'.join(alert_msg) or False @api.depends('user_id') def _compute_previous_order_ids(self): self.previous_order_ids = self._default_previous_order_ids() @api.one @api.depends('user_id') def _compute_cash_move_balance(self): domain = [('user_id', '=', self.user_id.id)] lunch_cash = self.env['lunch.cashmove'].read_group(domain, ['amount', 'user_id'], ['user_id']) if len(lunch_cash): self.cash_move_balance = lunch_cash[0]['amount'] self.balance_visible = (self.user_id == self.env.user) or self.user_has_groups('lunch.group_lunch_manager') @api.one @api.constrains('date') def _check_date(self): """ Prevents the user to create an order in the past """ date_order = datetime.datetime.strptime(self.date, '%Y-%m-%d') date_today = datetime.datetime.strptime(fields.Date.context_today(self), '%Y-%m-%d') if (date_order < date_today): raise ValidationError(_('The date of your order is in the past.')) @api.one @api.depends('order_line_ids.state') def _compute_order_state(self): """ Update the state of lunch.order based on its orderlines. Here is the logic: - if at least one order line is cancelled, the order is set as cancelled - if no line is cancelled but at least one line is not confirmed, the order is set as new - if all lines are confirmed, the order is set as confirmed """ if not self.order_line_ids: self.state = 'new' else: isConfirmed = True for orderline in self.order_line_ids: if orderline.state == 'cancelled': self.state = 'cancelled' return elif orderline.state == 'confirmed': continue else: isConfirmed = False if isConfirmed: self.state = 'confirmed' else: self.state = 'new' return
class AccountMove(models.Model): _inherit = 'account.move' document = fields.Char( string='Document', compute='_compute_document', store=True, readonly=True, ) document_id = fields.Reference( REFERENCE_SELECT, string='Document', compute='_compute_document', store=True, readonly=True, ) doctype = fields.Selection( DOCTYPE_SELECT, string='Doctype', compute='_compute_document', store=True, index=True, help="Use selection as refer_type in res_doctype", ) date_value = fields.Date( string='Value Date', compute='_compute_document', store=True, help="If origin document have value date. Otherwise, use move date", ) invoice_ids = fields.One2many( 'account.invoice', 'move_id', string='Invoice', readonly=True, ) invoice_cancel_ids = fields.One2many( 'account.invoice', 'cancel_move_id', string='Invoice Cancel', readonly=True, ) invoice_clear_prepaid_ids = fields.One2many( 'account.invoice', 'clear_prepaid_move_id', string='Invoice Clear Prepaid', readonly=True, ) voucher_ids = fields.One2many( 'account.voucher', 'move_id', string='Payment', readonly=True, ) voucher_cancel_ids = fields.One2many( 'account.voucher', 'cancel_move_id', string='Payment Cancel', readonly=True, ) voucher_recognize_vat_ids = fields.One2many( 'account.voucher', 'recognize_vat_move_id', string='Payment Recognize VAT', readonly=True, ) bank_receipt_ids = fields.One2many( 'account.bank.receipt', 'move_id', string='Bank Receipt', readonly=True, ) bank_receipt_cancel_ids = fields.One2many( 'account.bank.receipt', 'cancel_move_id', string='Bank Receipt Cancel', readonly=True, ) salary_expense_ids = fields.One2many( 'hr.salary.expense', 'move_id', string='Salary Expense', readonly=True, ) salary_expense_cancel_ids = fields.One2many( 'hr.salary.expense', 'cancel_move_id', string='Salary Expense Cancel', readonly=True, ) expense_rev_ic_ids = fields.One2many( 'hr.expense.expense', 'rev_ic_move_id', string='IC Revenue', readonly=True, ) expense_exp_ic_ids = fields.One2many( 'hr.expense.expense', 'exp_ic_move_id', string='IC Expense', readonly=True, ) account_interface_ids = fields.One2many( 'interface.account.entry', 'move_id', string='Account Interface', readonly=True, ) @api.multi @api.depends('invoice_ids.internal_number', 'invoice_cancel_ids.internal_number', 'invoice_clear_prepaid_ids.internal_number', 'voucher_ids.number', 'voucher_cancel_ids.number', 'voucher_recognize_vat_ids.number', 'bank_receipt_ids.name', 'bank_receipt_cancel_ids.name', 'salary_expense_ids.name', 'salary_expense_cancel_ids.name', 'expense_rev_ic_ids.number', 'expense_exp_ic_ids.number', 'account_interface_ids.number', 'ref', # check for stock.picking case, as it has no move_id ) def _compute_document(self): for rec in self: document = False # Invoice if rec.invoice_ids: document = rec.invoice_ids[0] elif rec.invoice_cancel_ids: document = rec.invoice_cancel_ids[0] elif rec.invoice_clear_prepaid_ids: document = rec.invoice_clear_prepaid_ids[0] # Voucher elif rec.voucher_ids: document = rec.voucher_ids[0] elif rec.voucher_cancel_ids: document = rec.voucher_cancel_ids[0] elif rec.voucher_recognize_vat_ids: document = rec.voucher_recognize_vat_ids[0] # Bank Receipt elif rec.bank_receipt_ids: document = rec.bank_receipt_ids[0] elif rec.bank_receipt_cancel_ids: document = rec.bank_receipt_cancel_ids[0] # Salary Expense elif rec.salary_expense_ids: document = rec.salary_expense_ids[0] elif rec.salary_expense_cancel_ids: document = rec.salary_expense_cancel_ids[0] # Expense IC elif rec.expense_rev_ic_ids: document = rec.expense_rev_ic_ids[0] elif rec.expense_exp_ic_ids: document = rec.expense_exp_ic_ids[0] # Account Interface elif rec.account_interface_ids: document = rec.account_interface_ids[0] elif rec.ref: # Last chance for picking, as it not have move_id Picking = self.env['stock.picking'] picking = Picking.search([('name', '=', rec.ref)]) document = picking and picking[0] or False # Assign reference if document: rec.document_id = '%s,%s' % (document._name, document.id) if document._name in ('stock.picking', 'account.bank.receipt'): rec.document = document.name elif document._name == 'account.invoice': rec.document = document.internal_number else: rec.document = document.number rec.doctype = self._get_doctype(document._name, document) if 'date_value' in document._fields: rec.date_value = document.date_value else: rec.doctype = 'adjustment' # <-- Not related to any doc if not rec.date_value: rec.date_value = rec.date # No Value Date, same as date @api.model def _get_doctype(self, model, document): if model == 'account.invoice': return INVOICE_DOCTYPE[document.journal_id.type] if model == 'account.voucher': return VOUCHER_DOCTYPE[document.type] if model == 'account.bank.receipt': return 'bank_receipt' if model == 'hr.expense.expense': return 'employee_expense' if model == 'hr.salary.expense': return 'salary_expense' if model == 'stock.picking': return PICKING_DOCTYPE[document.picking_type_id.code] if model == 'interface.account.entry': return 'interface_account'
class OpStudent(models.Model): _inherit = 'op.student' library_card_id = fields.Many2one('op.library.card', 'Library Card') book_movement_lines = fields.One2many('op.book.movement', 'student_id', 'Movements')