def link(session, job, backend_id_pos=2, Ymk_id_pos=3): """ Open a Ymk URL on the admin page to view/edit the record related to the job. """ binding_model = job.args[0] # shift one to the left because session is not in job.args backend_id = job.args[backend_id_pos - 1] Ymk_id = job.args[Ymk_id_pos - 1] env = get_environment(session, binding_model, backend_id) adapter = env.get_connector_unit(GenericAdapter) try: url = adapter.admin_url(Ymk_id) except ValueError: raise exceptions.Warning( _('No admin URL configured on the backend or ' 'no admin path is defined for this record.')) action = { 'type': 'ir.actions.act_url', 'target': 'new', 'url': url, } return action
def _send_email_to_registrations(self, body): template = self.env.ref( 'event_registration_mass_mailing.email_template_event_' 'registration', False) if not template: raise exceptions.Warning( _("Email template not found for event registration")) for event in self: for registration in event.registration_ids: wizard = self.env['mail.compose.message'].with_context( default_composition_mode='mass_mail', default_template_id=template.id, default_use_template=True, active_id=registration.id, active_ids=registration.ids, active_model='event.registration', default_model='event.registration', default_res_id=registration.id, ).create({ 'subject': template.subject, 'body': body }) wizard.send_mail()
def default_get(self, fields): res = super(RmaLineMakeSaleOrder, self).default_get(fields) rma_line_obj = self.env["rma.order.line"] rma_line_ids = self.env.context["active_ids"] or [] active_model = self.env.context["active_model"] if not rma_line_ids: return res assert active_model == "rma.order.line", "Bad context propagation" items = [] lines = rma_line_obj.browse(rma_line_ids) for line in lines: items.append([0, 0, self._prepare_item(line)]) customers = lines.mapped("partner_id") if len(customers) == 1: res["partner_id"] = customers.id else: raise exceptions.Warning( _("Only RMA lines from the same partner can be processed at " "the same time")) res["item_ids"] = items return res
def _get_membership_interval(self, product, date): """Get the interval to evaluate as the theoretical membership period. :param product: Product that defines the membership :param date: date object for the requested date to determine the variable period :return: A tuple with 2 date objects with the beginning and the end of the period """ if product.membership_type == 'fixed': return super(AccountInvoiceLine, self)._get_membership_interval( product, date) if product.membership_interval_unit == 'days': raise exceptions.Warning( _("It's not possible to prorrate daily periods.")) if product.membership_interval_unit == 'weeks': weekday = date.weekday() date_from = date - datetime.timedelta(weekday) elif product.membership_interval_unit == 'months': date_from = datetime.date(day=1, month=date.month, year=date.year) elif product.membership_interval_unit == 'years': date_from = datetime.date(day=1, month=1, year=date.year) date_to = product._get_next_date(date) - datetime.timedelta(1) return date_from, date_to
def generate(self): view_ref = self.pool.get('ir.model.data').get_object_reference( self._cr, self._uid, 'seo_data_manager', 'seo_product_success_update_view') view_id = view_ref and view_ref[1] or False, self.generate_data(self.title, 'title') self.generate_data(self.keyword, 'keyword') self.generate_data(self.description, 'description') if self.title or self.keyword or self.description: return { 'type': 'ir.actions.act_window', 'name': 'SEO Product Data Updated', 'view_mode': 'form', 'view_type': 'form', 'view_id': view_id, 'res_model': 'success.update', 'target': 'new', 'context': self._context, } if not (self.title or self.keyword or self.description): raise exceptions.Warning( _('There is not any update for Product Meta data ')) return True
def default_get(self, fields): res = super(RmaLineMakeSaleOrder, self).default_get(fields) rma_line_obj = self.env['rma.order.line'] rma_line_ids = self.env.context['active_ids'] or [] active_model = self.env.context['active_model'] if not rma_line_ids: return res assert active_model == 'rma.order.line', 'Bad context propagation' items = [] lines = rma_line_obj.browse(rma_line_ids) for line in lines: items.append([0, 0, self._prepare_item(line)]) customers = lines.mapped('partner_id') if len(customers) == 1: res['partner_id'] = customers.id else: raise exceptions.Warning( _('Only RMA lines from the same partner can be processed at ' 'the same time')) res['item_ids'] = items return res
def action_confirm(self): RepositoryConfirm = self.env['repository.confirm'] action = self.env.context.get('action') KNOWN_ACTIONS = {'remove', 'update', 'enable', 'disable'} if action not in KNOWN_ACTIONS: raise exceptions.Warning(_('Unkown action: %s') % (action,)) if not self.env.user.has_group('base.group_system'): raise exceptions.AccessDenied return { 'name': _(RepositoryConfirm._description), 'type': 'ir.actions.act_window', 'view_type': 'form', 'view_mode': 'form', 'target': 'new', 'res_model': 'repository.confirm', 'res_id': RepositoryConfirm.create({ 'repository_id': self.id, 'state': action }).id }
def create(self, vals): if self._context.get('company_partner', False): vals['farm'] = True elif vals.get('farm', False): # El partner ha sido creado a mano. raise exceptions.Warning( _('Farm creation error'), _('To create farm must do it from the menu farm High')) res = super(ResPartner, self).create(vals) if vals.get('exploitation_technician', False): res.message_subscribe([res.exploitation_technician.partner_id.id]) if vals.get('secondary_technician', False): res.message_subscribe([res.secondary_technician.partner_id.id]) if res.employees_quantity or res.employees_date: count_args = { 'partner_id': res.id, 'date': res.employees_date or date.today(), 'user_id': self.env.user.id, 'quantity': res.employees_quantity, 'state': 'current' } self.env['employee.farm.count'].create(count_args) return res
def modify_production_order_state(self, action): """ Modifies production order state if work order state is changed. @param action: Action to perform. @return: Nothing """ self.ensure_one() prod = self.production_id if action == 'start': if prod.state == 'confirmed': prod.force_production() prod.signal_workflow('button_produce') elif prod.state in ('ready', 'in_production', 'finished', 'validated', 'closed'): prod.signal_workflow('button_produce') else: raise exceptions.Warning( _('Error!'), _('Manufacturing order cannot be started in state "%s"!') % (prod.state)) else: return super(MrpProductionWorkcenterLine, self).modify_production_order_state(action) return
def _prepare_purchase_order(self, picking_type, location, company_id): if not self.supplier_id: raise exceptions.Warning(_('Enter a supplier.')) supplier = self.supplier_id supplier_pricelist = supplier.property_product_pricelist or False data = { 'origin': '', 'partner_id': self.supplier_id.id, 'pricelist_id': supplier_pricelist.id, 'location_id': location.id, 'fiscal_position': (supplier.property_account_position_id and supplier.property_account_position_id.id) or False, 'picking_type_id': picking_type.id, 'company_id': company_id, } return data
def delegate(self): self.ensure_one() child_ids = self.env['compassion.child'].browse( self.env.context.get('active_ids')).filtered('is_available') if self.date_end_delegation: if datetime.strptime(self.date_delegation, DF) > \ datetime.strptime(self.date_end_delegation, DF): raise exceptions.Warning( "Invalid value", _("End date must be later than beginning")) if datetime.strptime(self.date_delegation, DF) <= datetime.today(): child_ids.write({'state': 'D'}) child_ids.write({ 'delegated_to': self.partner.id, 'delegated_comment': self.comment, 'date_delegation': self.date_delegation, 'date_end_delegation': self.date_end_delegation }) return True
def _compute_amount(self): # self.amount_tax = sum(line.amount for line in self.tax_line) # self.amount_total = self.amount_untaxed + self.amount_tax super(account_invoice, self)._compute_amount() # Taxes are applied line by line, we cannot apply a # discount on taxes that are not proportional if not all(t.type == 'percent' for line in self.invoice_line \ for t in line.invoice_line_tax_id): raise exceptions.Warning(_('Unable to compute a global ' 'discount with non percent-type ' 'taxes')) cur = self.currency_id self.base_amount = sum(line.price_subtotal \ for line in self.invoice_line) # 1. partner discount self.partner_disc_amt = cur.round(self.base_amount *\ (-1 * self.partner_disc / 100)) base_amt_part_disc = self.base_amount + self.partner_disc_amt # 2. additional discount self.add_disc_amt = cur.round(base_amt_part_disc *\ (-1 * self.add_disc / 100)) self.amount_untaxed = base_amt_part_disc + self.add_disc_amt self.amount_total = self.amount_untaxed + self.amount_tax
def _make_production_consume_line(self, line): if not line.product_id: product_obj = self.env['product.product'] att_values_ids = [ attr_line.value and attr_line.value.id or False for attr_line in line.product_attributes ] domain = [('product_tmpl_id', '=', line.product_template.id)] for value in att_values_ids: if not value: raise exceptions.Warning( _("You can not confirm before configuring all" " attribute values.")) domain.append(('attribute_value_ids', '=', value)) product = product_obj.search(domain) if not product: product = product_obj.create({ 'product_tmpl_id': line.product_template.id, 'attribute_value_ids': [(6, 0, att_values_ids)] }) line.product_id = product return super(MrpProduction, self)._make_production_consume_line(line)
def _default_claim_line_ids(self): # TODO use custom states to show buttons of this wizard or not instead # of raise an error picking_type = self.env.context.get('picking_type') if isinstance(picking_type, int): picking_type_code = self.env['stock.picking.type'].\ browse(picking_type).code picking_type = 'in' if picking_type_code == 'incoming' else 'out' move_field = 'move_in_id' if picking_type == 'in' else 'move_out_id' # Search claim lines related to the current claim with no active # picking moves (or not at all) domain = [('claim_id', '=', self.env.context['active_id']), '|', (move_field, '=', False), (move_field + '.state', '=', 'cancel')] line_ids = self.env['claim.line'].search(domain) if not line_ids: raise exceptions.Warning( _('Error'), _('A picking has already been' ' created for this claim.')) return line_ids
def onchange_template_id(self, template_id, partner=False, fiscal_position=False, order_line=False): if not template_id: return {} if not partner: raise exceptions.Warning(_('You must enter the customer')) data = {} lines = [(6, 0, [])] for line in order_line: if line[0] == 6 and line[1] == 0 and line[2]: for line_id in line[2]: lines.append((4, line_id)) elif line[0] == 0 and line[1] == 0: lines.append(line) res = super(SaleOrder, self).onchange_template_id( template_id, partner=partner, fiscal_position=fiscal_position) value = res.get('value') if 'note' in value and value.get('note'): data['note'] = value.get('note') if ('website_description' in value and value.get('website_description')): data['website_description'] = value.get('website_description') if ('validity_date' in value and value.get('validity_date')): data['validity_date'] = value.get('validity_date') if ('options' in value and value.get('options')): data['options'] = value.get('options') order_lines = [] if ('order_line' in value and value.get('order_line')): order_lines = value.get('order_line') for line in order_lines: if line[0] != 5 and line[0] == 0 and line[1] == 0: lines.append(line) elif line[0] != 5 and line[0] == 6 and line[2]: for line_id in line[2]: lines.append((4, line_id)) data['order_line'] = lines return {'value': data}
def _generate_nacex_expedition(self, webservice_class=None, package_ids=None): self.ensure_one() user = self.env.user company = user.company_id if webservice_class is None: webservice_class = NacexWebService if package_ids is None: packages = self._get_packages_from_picking() packages = sorted(packages, key=attrgetter('name')) else: # restrict on the provided packages package_obj = self.env['stock.quant.package'] packages = package_obj.browse(package_ids) web_service = webservice_class(company) res = web_service.generate_expedition(self, packages) if res['errors'] == True: self.action_error_create_shipping_expedition_message_slack( res) #slack.message raise exceptions.Warning('\n'.join(res['error'])) else: return { 'code': res['return']['result']['expe_codigo'], 'delivery_code': res['return']['result']['ag_cod_num_exp'], 'date': res['return']['result']['fecha_objetivo'], 'hour': res['return']['result']['hora_entrega'], 'observations': res['return']['result']['cambios'], 'state': 'generate', 'state_code': 2, 'exps_rels': '', } return res['return']['result']
def button_registration_open(self): self.ensure_one() wiz_obj = self.env['wiz.event.append.assistant'] if self.date_start and self.date_end: registrations = self.event_id.registration_ids.filtered( lambda x: x.id != self.id and x.partner_id and x.date_start and x.date_end and x.state in ('done', 'open') and x.partner_id.id == self.partner_id.id and ((self.date_end >= x.date_start and self.date_end <= x.date_end ) or (self.date_start <= x.date_end and self.date_start >= x. date_start))) if registrations: raise exceptions.Warning( _('You can not confirm this registration, because their' ' dates overlap with another record of the same' ' employee')) wiz = wiz_obj.with_context({ 'active_id': self.event_id.id, 'active_ids': self.event_id.ids, 'active_model': 'event.event' }).create(self._prepare_wizard_registration_open_vals()) context = self.env.context.copy() context.update({ 'active_id': self.event_id.id, 'active_ids': self.event_id.ids, 'active_model': 'event.event', }) return { 'name': _('Add Person To Session'), 'type': 'ir.actions.act_window', 'res_model': 'wiz.event.append.assistant', 'view_type': 'form', 'view_mode': 'form', 'res_id': wiz.id, 'target': 'new', 'context': context }
def generate_purchase_orders(self): purchase_obj = self.env['purchase.order'] purchase_line_obj = self.env['purchase.order.line'] supplierinfo_obj = self.env['product.supplierinfo'] for requisition in self: for line in requisition.line_ids: product = line.product_id condition = [('product_tmpl_id', '=', product.product_tmpl_id.id)] supplierinfos = supplierinfo_obj.search(condition) if not supplierinfos: raise exceptions.Warning( _('You must define one supplier for the product: %s') % product.name) else: for supplierinfo in supplierinfos: supplier = supplierinfo.name try: vals = self._prepare_purchase_order_line( requisition, line, False, supplier) condition = [('partner_id', '=', supplier.id), ('state', '=', 'draft'), ('requisition_id', '=', requisition.id)] purchase_order = purchase_obj.search(condition) if not purchase_order: po_vals = self._prepare_purchase_order( requisition, supplier) purchase = purchase_obj.create(po_vals) else: purchase = purchase_order[0] vals.update({'order_id': purchase.id}) purchase_line_obj.create(vals) except: _logger.info(_('Supplier: %s - Product: %s') % (supplier.name, product.name)) return True
def _generate_txt_expedition(self, webservice_class=None, package_ids=None): self.ensure_one() user = self.env.user company = user.company_id if webservice_class is None: webservice_class = TxtWebService if package_ids is None: packages = self._get_packages_from_picking() packages = sorted(packages, key=attrgetter('name')) else: # restrict on the provided packages package_obj = self.env['stock.quant.package'] packages = package_obj.browse(package_ids) web_service = webservice_class(company, self.env) #generate_expedition res = web_service.generate_expedition(self, packages) if res['errors'] == True: self.action_error_create_shipping_expedition_message_slack( res) #slack.message raise exceptions.Warning('\n'.join(res['error'])) else: return { 'code': '', 'delivery_code': 'Generado ' + str(self.name), 'date': datetime.today().strftime("%Y-%m-%d"), 'hour': '', 'observations': '', 'state': 'generate', 'state_code': 2, 'exps_rels': '', }
def _get_label_data(self): data = super(StockPicking, self)._get_label_data() if self.delivery_address_id: partner = self.delivery_address_id #comprobacion de todos los campos al principio must_fields = { 'No Street in Shipping address': partner.street, 'No Zip in Shipping address': partner.zip, 'No City in Shipping address': partner.city, } for key, value in must_fields.items(): if not value: raise exceptions.Warning(_('%s' % key)) data.update({ 'cliente_direccion': unidecode(partner.street + ((' ' + partner.street2) or '')), 'cliente_poblacion': unidecode(partner.city), 'cliente_cpostal': unidecode(partner.zip), 'cliente_pais': unidecode(partner.country_id.code), }) if self.consignee_id: data.update({ 'cliente_nombre': unidecode(self.consignee_id.name), 'cliente_email': unidecode(self.consignee_id.email or u''), 'cliente_telefono': unidecode(self.consignee_id.phone or self.consignee_id.mobile or u''), 'cliente_atencion': unidecode(self.consignee_id.name), }) return data
def button_print_from_price_history(self): wiz = self[0] price_historys = self.env['product.price.history'].search([ ('datetime', '>=', wiz.date_from) ]) product_ids = [] if wiz.price_history_quantity == 'one': product_tmpl_ids = [ ph.product_template_id.id for ph in price_historys ] products = self.env['product.product'].search([ ('product_tmpl_id', 'in', product_tmpl_ids) ]) product_ids = list(set([p.id for p in products])) elif wiz.price_history_quantity == 'total': for ph in price_historys: found_products = self.env['product.product'].search( [('product_tmpl_id', '=', ph.product_template_id.id)], limit=1) found_product_ids = [p.id for p in found_products] if found_products.exists() \ and found_products[0].id not in product_ids: found_product_ids *= int( ph.product_template_id.qty_available) product_ids = product_ids + found_product_ids product_ids = filter(lambda x: x, product_ids) if not product_ids: raise exceptions.Warning(_('No labels for print')) else: return { 'type': 'ir.actions.report.xml', 'report_name': wiz.report_id.report_name, 'datas': { 'ids': product_ids } }
def launch_wizard(self): """Search for a wizard to launch according to the type. If type is manual. just confirm the order. Previously (pre-v6) in account_payment/wizard/wizard_pay.py """ context = self.env.context.copy() order = self[0] # check if a wizard is defined for the first order if order.mode.type and order.mode.type.ir_model_id: context['active_ids'] = self.ids wizard_model = order.mode.type.ir_model_id.model wizard_obj = self.env[wizard_model] return { 'name': wizard_obj._description or _('Payment Order Export'), 'view_type': 'form', 'view_mode': 'form', 'res_model': wizard_model, 'domain': [], 'context': context, 'type': 'ir.actions.act_window', 'target': 'new', 'nodestroy': True, } else: # should all be manual orders without type or wizard model for order in self[1:]: if order.mode.type and order.mode.type.ir_model_id: raise exceptions.Warning( _('Error'), _('You can only combine payment orders of the same ' 'type')) # process manual payments for order_id in self.ids: workflow.trg_validate(self.env.uid, 'payment.order', order_id, 'done', self.env.cr) return {}
def init(self, cr): if self._name != 'l10n.es.aeat.report': seq_obj = self.pool['ir.sequence'] try: aeat_num = getattr(self, '_aeat_number') if not aeat_num: raise Exception() sequence = "aeat%s-sequence" % aeat_num if not seq_obj.search(cr, SUPERUSER_ID, [('name', '=', sequence)]): seq_vals = { 'name': sequence, 'code': 'aeat.sequence.type', 'number_increment': 1, 'implementation': 'no_gap', 'padding': 9, 'number_next_actual': 1, 'prefix': aeat_num + '-' } seq_obj.create(cr, SUPERUSER_ID, seq_vals) except: raise exceptions.Warning( "Modelo no válido: %s. Debe declarar una variable " "'_aeat_number'" % self._name)
def button_confirm(self): for line in self: if not line.product_id: product_obj = self.env['product.product'] att_values_ids = [ attr_line.value and attr_line.value.id or False for attr_line in line.product_attributes ] domain = [('product_tmpl_id', '=', line.product_template.id)] for value in att_values_ids: if not value: raise exceptions.Warning( _("You can not confirm before configuring all" " attribute values.")) domain.append(('attribute_value_ids', '=', value)) product = product_obj.search(domain) if not product: product = product_obj.create({ 'product_tmpl_id': line.product_template.id, 'attribute_value_ids': [(6, 0, att_values_ids)] }) line.write({'product_id': product.id}) super(SaleOrderLine, self).button_confirm()
def scrap_qty_left(self): self.ensure_one() if self.left_product_qty <= 0: raise exceptions.Warning(_('You can not scrap negative quantity.')) view_id = self.env.ref('stock.view_stock_move_scrap_wizard') return { 'view_type': 'form', 'view_mode': 'tree', 'res_model': 'stock.move.scrap', 'views': [(view_id.id, 'form')], 'view_id': False, 'type': 'ir.actions.act_window', 'target': 'new', 'context': self.with_context(active_ids=self.move_created_ids2.ids, active_id=self.move_created_ids2[:1].id, qty_left=self.left_product_qty).env.context, }
def _check_global_default(self, cr, uid, vals, matching_filters, context=None): """ _check_global_default(cursor, UID, dict, list(dict), dict) -> None Checks if there is a global default for the model_id requested. If there is, and the default is different than the record being written (-> we're not updating the current global default), raise an error to avoid users unknowingly overwriting existing global defaults (they have to explicitly remove the current default before setting a new one) This method should only be called if ``vals`` is trying to set ``is_default`` :raises openerp.exceptions.Warning: if there is an existing default and we're not updating it """ existing_default = self.search(cr, uid, [('model_id', '=', vals['model_id']), ('user_id', '=', False), ('is_default', '=', True)], context=context) if not existing_default: return if matching_filters and \ (matching_filters[0]['id'] == existing_default[0]): return raise exceptions.Warning( _("There is already a shared filter set as default for %(model)s, delete or change it before setting a new default" ) % {'model': vals['model_id']})
def check_category(self, line): # obtener la categoria que identifica al proveedor, es la root del arbol supp_categ = self.supplier.categ_id if not supp_categ: raise exceptions.Warning(_("Supplier without category")) # cargar los descuentos para el proveedor supp_categ.update_discounts(line.get_discounts('dp')) # preparar el return res = supp_categ # si hay una categoria en la linea procesar esto if line.categ: # ver si existe la categoria bajo el root cat = supp_categ.search_child(line.categ) # si la categoria no existe, la creo if not cat: cat = supp_categ.create_child(line.categ) # cargo los descuentos para esta categoria cat.update_discounts(line.get_discounts('dc')) # preparo el return res = cat # veo si hay una subcategoria en la linea if line.sub_categ: # verifico si existe en los hijos de la categoria sub = cat.search_child(line.sub_categ) # si no existe la agrego if not sub: sub = cat.create_child(line.sub_categ) # aplico los descuentos de la subcategoria sub.update_discounts(line.get_discounts('ds')) # preparo el return res = sub # devuelvo la ultima categoria que procesé return res
def action_create_mrp(self): if self.product_uom_qty <= 0: raise exceptions.Warning(_('The quantity must be positive.')) attribute_list = [] for attribute_line in self.product_attribute_ids: values = { 'attribute_id': attribute_line.attribute_id.id, 'value_id': attribute_line.value_id.id, 'product_tmpl_id': self.product_tmpl_id.id, 'owner_model': 'mrp.production', } try: values['custom_value'] = attribute_line.custom_value except: pass attribute_list.append(values) mrp = self.env['mrp.production'].create({ 'product_tmpl_id': self.product_tmpl_id.id or False, 'product_id': self.product_id.id or False, 'product_qty': self.product_uom_qty, 'product_uom': self.product_uom.id, 'product_attribute_ids': [(0, 0, x) for x in attribute_list], 'active': False, }) mrp.write({ 'sale_order': self.order_id.id, 'sale_line': self.id, 'partner': self.order_id.partner_id.id, }) mrp.with_context(sale_line=self.id).action_compute() self.mrp_production_id = mrp
def create(self, data): workcenter_obj = self.env['mrp.routing.workcenter'] if data.get('routing_wc_line', False): work = workcenter_obj.browse(data.get('routing_wc_line')) if work.qtemplate_ids: data.update({ 'routing_workcenter_qtemplate_ids': [(6, 0, work.qtemplate_ids.ids)] }) find_test = False if data.get('required_test', False): if not data.get('qtemplate_id', False): raise exceptions.Warning( _('Error!, You must define the test template')) else: data.update({'qtemplate_id': False}) find_test = True if find_test: if 'routing_wc_line' in data: data.update({'required_test': work.operation.required_test}) if work.operation.qtemplate_id: data.update( {'qtemplate_id': work.operation.qtemplate_id.id}) return super(MrpProductionWorkcenterLine, self).create(data)
def _create_account_for_not_employee_from_wizard(self, event, registration): account_obj = self.env['account.analytic.account'] analytic_invoice_line_obj = self.env['account.analytic.invoice.line'] if event.sale_order.payer == 'school': super(WizEventAppendAssistant, self)._create_account_for_not_employee_from_wizard( event, registration) else: if len(event.event_ticket_ids) == 0: raise exceptions.Warning(_('Event without ticket')) vals = self._prepare_data_for_account_not_employee( event, registration) new_account = account_obj.create(vals) registration.analytic_account = new_account.id lines = event.event_ticket_ids.filtered( lambda x: x.is_pa_partner == registration.partner_id. commercial_partner_id.is_pa_partner) if not lines: lines = event.event_ticket_ids line_vals = { 'analytic_account_id': new_account.id, 'name': (lines[0].sale_line.name or lines[0].product_id.name), 'price_unit': lines[0].price, 'price_subtotal': lines[0].sale_line.price_subtotal, 'product_id': lines[0].product_id.id, 'quantity': lines[0].sale_line.product_uom_qty, 'uom_id': (lines[0].sale_line.product_uom.id or lines[0].product_id.uom_id.id) } analytic_invoice_line_obj.create(line_vals)