def make_advance_payment(self): """Create customer paylines and validates the payment""" self.ensure_one() payment_obj = self.env["account.payment"] purchase_obj = self.env["purchase.order"] purchase_ids = self.env.context.get("active_ids", []) if purchase_ids: purchase_id = fields.first(purchase_ids) purchase = purchase_obj.browse(purchase_id) payment_vals = self._prepare_payment_vals(purchase) payment = payment_obj.create(payment_vals) purchase.account_payment_ids |= payment payment.action_post() return { "type": "ir.actions.act_window_close", }
def _apply_strategy_empty_bin(self, quants): need = yield # Group by location (in this removal strategies, we want to consider # the total quantity held in a location). quants_per_bin = quants._group_by_location() # We take goods only if we empty the bin. # The original ordering (fefo, fifo, ...) must be kept. rounding = fields.first(quants).product_id.uom_id.rounding for location, location_quants in quants_per_bin: location_quantity = sum(location_quants.mapped("quantity")) - sum( location_quants.mapped("reserved_quantity")) if location_quantity <= 0: continue if float_compare(need, location_quantity, rounding) != -1: need = yield location, location_quantity, need
def default_get(self, fields): res = super().default_get(fields) if self.env.context.get('active_model', False) != 'stock.quant': return res # Load data directly from quants quants = self.env['stock.quant'].browse( self.env.context.get('active_ids', False)) res['stock_move_location_line_ids'] = [(0, 0, { 'product_id': quant.product_id.id, 'move_quantity': quant.quantity, 'max_quantity': quant.quantity, 'origin_location_id': quant.location_id.id, 'lot_id': quant.lot_id.id, 'product_uom_id': quant.product_uom_id.id, 'custom': False, }) for quant in quants] res['origin_location_id'] = first(quants).location_id.id return res
def write(self, vals): res = super(AttributeGroup, self).write(vals) if "name" in vals.keys(): mass_editings = self.env["mass.editing"].search([ ("attribute_group_id", "in", self.ids) ]) for group in self: mass_editing = first( mass_editings.filtered( lambda o: o.attribute_group_id == group)) if mass_editing: mass_editing.name = group.name # TODO: we should use tests.Form IMO mass_editing.action_name = mass_editing._prepare_action_name( ) mass_editing.disable_mass_operation() mass_editing.enable_mass_operation() return res
def action_undo_last_scan(self): res = super().action_undo_last_scan() log_scan = first(self.scan_log_ids.filtered( lambda x: x.create_uid == self.env.user)) if log_scan: inventory_line = self.env['stock.inventory.line'].search( self._prepare_inventory_line_domain(log_scan=log_scan)) if inventory_line.inventory_id.state == 'done': raise ValidationError(_( 'You can not remove a scanning log from an inventory ' 'validated') ) if inventory_line: qty = inventory_line.product_qty - log_scan.product_qty inventory_line.product_qty = max(qty, 0.0) self.inventory_product_qty = inventory_line.product_qty log_scan.unlink() return res
def action_undo_last_scan(self): res = super().action_undo_last_scan() log_scan = first(self.scan_log_ids.filtered( lambda x: x.create_uid == self.env.user)) if log_scan: move_line = self.env['stock.move.line'].search( self._prepare_internal_transfer_move_line_domain(log_scan=log_scan) ) if move_line.picking_id.state == 'done': raise ValidationError(_( 'You can not remove a scanning log from an validated') ) if move_line: qty = move_line.qty_done - log_scan.product_qty move_line.qty_done = max(qty, 0.0) self.picking_product_qty = move_line.qty_done log_scan.unlink() return res
def _get_invoice_line_values(self, moves, invoice_values, invoice): """ Create invoice line values from given moves :param moves: stock.move :param invoice: account.invoice :return: dict """ values = super(StockInvoiceOnshipping, self)._get_invoice_line_values(moves, invoice_values, invoice) move = fields.first(moves) values['cfop_id'] = move.cfop_id.id values['operation_id'] = move.operation_id.id or \ move.picking_id.operation_id.id values['operation_line_id'] = move.operation_line_id.id values['partner_id'] = invoice.partner_id.id return values
def delete_empty_lines(self, delete_empty_rows=False): self.ensure_one() for name in list(set(self.line_ids.mapped("value_y"))): rows = self.line_ids.filtered(lambda l: l.value_y == name) if not rows: continue row = fields.first(rows) if delete_empty_rows and self._is_add_line(row): check = any([line.unit_amount for line in rows]) else: check = not all([line.unit_amount for line in rows]) if not check: continue row_lines = self.timesheet_ids.filtered( lambda aal: self._is_line_of_row(aal, row)) row_lines.filtered( lambda t: t.name == empty_name and not t.unit_amount).unlink() if self.timesheet_ids != self.timesheet_ids.exists(): self._sheet_write("timesheet_ids", self.timesheet_ids.exists())
def _get_invoice_line_values(self, moves, invoice): """ Create invoice line values from given moves :param moves: stock.move :param invoice: account.invoice :return: dict """ result = {} values = super(StockInvoiceOnshipping, self)._get_invoice_line_values(moves, invoice) move = fields.first(moves) if move.procurement_id and move.procurement_id.sale_line_id: result['partner_order'] = \ move.procurement_id.sale_line_id.customer_order result['partner_order_line'] = \ move.procurement_id.sale_line_id.customer_order_line values.update(result) values = self._simulate_invoice_line_onchange(values) return values
def _get_journal(self): """ Get the journal depending on the journal_type :return: account.journal recordset """ self.ensure_one() if self.fiscal_operation_journal: pickings = self._load_pickings() picking = fields.first(pickings) journal = picking.fiscal_operation_id.journal_id if not journal: raise UserError( _('Invalid Journal! There is not journal defined' ' for this company: %s in fiscal operation: %s !') % (picking.company_id.name, picking.fiscal_operation_id.name)) else: journal = super()._get_journal() return journal
def _prepare_payment_line_vals(self, payment_order): self.ensure_one() assert payment_order, 'Missing payment order' aplo = self.env['account.payment.line'] # default values for communication_type and communication communication_type = 'normal' communication = self.move_id.ref or self.move_id.name # change these default values if move line is linked to an invoice if self.invoice_id: if self.invoice_id.reference_type != 'none': communication = self.invoice_id.reference ref2comm_type =\ aplo.invoice_reference_type2communication_type() communication_type =\ ref2comm_type[self.invoice_id.reference_type] else: if (self.invoice_id.type in ('in_invoice', 'in_refund') and self.invoice_id.reference): communication = self.invoice_id.reference if self.currency_id: currency_id = self.currency_id.id amount_currency = self.amount_residual_currency else: currency_id = self.company_id.currency_id.id amount_currency = self.amount_residual # TODO : check that self.amount_residual_currency is 0 # in this case if payment_order.payment_type == 'outbound': amount_currency *= -1 partner_bank_id = self.partner_bank_id.id or first( self.partner_id.bank_ids).id vals = { 'order_id': payment_order.id, 'partner_bank_id': partner_bank_id, 'partner_id': self.partner_id.id, 'move_line_id': self.id, 'communication': communication, 'communication_type': communication_type, 'currency_id': currency_id, 'amount_currency': amount_currency, # date is set when the user confirms the payment order } return vals
def _redirect_existing_url(self): """ During unbind, we have to redirect existing urls to the (first) related commerce category. :return: bool """ for record in self.filtered(lambda p: p.url_url_ids): # Active category without children categs = record.commerce_categ_ids.filtered( lambda c: c.active and not c.commerce_child_ids) if categs: categ = fields.first(categs) record.url_url_ids.write({ "redirect": True, "model_id": "{},{}".format(categ._name, categ.id), }) return True
def delete_empty_lines(self, delete_empty_rows=False): for name in list(set(self.line_ids.mapped('value_y'))): row = self.line_ids.filtered(lambda l: l.value_y == name) if row: row_0 = fields.first(row) is_add_line = self.add_line_project_id == row_0.project_id \ and self.add_line_task_id == row_0.task_id if delete_empty_rows and is_add_line: check = any([l.unit_amount for l in row]) else: check = not all([l.unit_amount for l in row]) if check: ts_row = self.timesheet_ids.filtered( lambda t: t.project_id.id == row_0.project_id.id and t. task_id.id == row_0.task_id.id) ts_row.filtered(lambda t: t.name == empty_name and not t. unit_amount).unlink() self._sheet_write('timesheet_ids', self.timesheet_ids.exists())
def _compute_lot(self): moves = self.filtered(lambda i: i.object_id and i.object_id._name == 'stock.move').mapped('object_id') move_lines = self.env['stock.move.line'].search([ ('lot_id', '!=', False), ('move_id', 'in', [move.id for move in moves]) ]) for inspection in self: if inspection.object_id: if inspection.object_id._name == 'stock.move.line': inspection.lot_id = \ inspection.object_id.lot_id elif inspection.object_id._name == 'stock.move': inspection.lot_id = first( move_lines.filtered(lambda line: line.move_id == inspection.object_id)).lot_id elif inspection.object_id._name == 'stock.production.lot': inspection.lot_id = inspection.object_id
def _action_generate_invoices(self): """ Action to generate invoices based on pickings :return: account.invoice recordset """ pickings = self._load_pickings() company = pickings.mapped("company_id") if company and company != self.env.user.company_id: raise UserError(_("All pickings are not related to your company!")) pick_list = self._group_pickings(pickings) invoices = self.env['account.invoice'].browse() for pickings in pick_list: moves = pickings.mapped("move_lines") grouped_moves_list = self._group_moves(moves) parts = self.ungroup_moves(grouped_moves_list) # The field document_type_id are in a function of operation_line_id # but the method used to create lines need to the invoice was created # to called, by this reason we need to get this information before the # FOR code used to create the invoice lines # TODO - Are better way to make it ? move = fields.first(pickings.move_lines) document_type_id = move.operation_line_id.get_document_type( move.picking_id.company_id).id for moves_list in parts: invoice, invoice_values = self.with_context( document_type_id=document_type_id )._build_invoice_values_from_pickings(pickings) lines = [(5, 0, {})] for moves in moves_list: line_values = self._get_invoice_line_values( moves, invoice_values, invoice) if line_values: lines.append((0, 0, line_values)) if line_values: # Only create the invoice if it have lines invoice_values['invoice_line_ids'] = lines invoice = self._create_invoice(invoice_values) invoice._onchange_invoice_line_ids() invoice.compute_taxes() invoices |= invoice return invoices
def _get_invoice_line_values(self, moves, invoice_values, invoice): """ Create invoice line values from given moves :param moves: stock.move :param invoice: account.invoice :return: dict """ move = fields.first(moves) values = move._prepare_br_fiscal_dict() values.update({ key: value for key, value in super()._get_invoice_line_values( moves, invoice_values, invoice).items() if value }) values['invoice_line_tax_ids'] = [ (6, 0, self.env['l10n_br_fiscal.tax'].browse( values['fiscal_tax_ids'][0][2]).account_taxes().ids) ] return values
def _create_returns(self): # return wizard cannot hold few lines for one move the implementation of # stock_account will raise singltone error, to propagate lot_id we are # mapping moves between pickings after move was created res = super()._create_returns() picking_returned = self.env["stock.picking"].browse(res[0]) ml_ids_to_update = picking_returned.move_line_ids.ids moves_with_lot = self.product_return_moves.mapped( "move_id.move_line_ids").filtered(lambda l: l.lot_id) for line in moves_with_lot: ml = fields.first( picking_returned.move_line_ids.filtered( lambda l: l.product_id == line.product_id and l.id in ml_ids_to_update)) if ml and not ml.lot_id and (ml.product_uom_qty == line.qty_done): ml.lot_id = line.lot_id ml_ids_to_update.remove(ml.id) return res
def _get_invoice_line_values(self, moves, invoice_values, invoice): """ Create invoice line values from given moves :param moves: stock.move :param invoice: account.invoice :return: dict """ values = super()._get_invoice_line_values(moves, invoice_values, invoice) move = fields.first(moves) fiscal_values = move._prepare_br_fiscal_dict() # A Fatura não pode ser criada com os campos price_unit e fiscal_price # negativos, o metodo _prepare_br_fiscal_dict retorna o price_unit # negativo, por isso é preciso tira-lo antes do update, e no caso do # fiscal_price é feito um update para caso do valor ser diferente do # price_unit del fiscal_values["price_unit"] fiscal_values["fiscal_price"] = abs(fiscal_values.get("fiscal_price")) # Como é usada apenas uma move para chamar o _prepare_br_fiscal_dict # a quantidade/quantity do dicionario traz a quantidade referente a # apenas a essa linha por isso é removido aqui. del fiscal_values["quantity"] # Mesmo a quantidade estando errada por ser chamada apenas por uma move # no caso das stock.move agrupadas e os valores fiscais e de totais # retornados poderem estar errados ao criar o documento fiscal isso # será recalculado já com a quantidade correta. values.update(fiscal_values) values["invoice_line_tax_ids"] = [( 6, 0, self.env["l10n_br_fiscal.tax"].browse( fiscal_values["fiscal_tax_ids"][0][2]).account_taxes().ids, )] return values
def _apply_strategy_packaging(self, quants): need = yield # Group by location (in this removal strategies, we want to consider # the total quantity held in a location). quants_per_bin = quants._group_by_location() product = fields.first(quants).product_id packaging_type_filter = self.packaging_type_ids # we'll walk the packagings from largest to smallest to have the # largest containers as possible (1 pallet rather than 10 boxes) packaging_quantities = sorted( product.packaging_ids.filtered( lambda packaging: (packaging.qty > 0 and ( packaging.packaging_type_id in packaging_type_filter if packaging_type_filter else True))).mapped("qty"), reverse=True, ) rounding = product.uom_id.rounding def is_greater_eq(value, other): return float_compare(value, other, precision_rounding=rounding) >= 0 for location, location_quants in quants_per_bin: location_quantity = sum(location_quants.mapped("quantity")) - sum( location_quants.mapped("reserved_quantity")) if location_quantity <= 0: continue for pack_quantity in packaging_quantities: enough_for_packaging = is_greater_eq(location_quantity, pack_quantity) asked_at_least_packaging_qty = is_greater_eq( need, pack_quantity) if enough_for_packaging and asked_at_least_packaging_qty: # compute how much packaging we can get take = (need // pack_quantity) * pack_quantity need = yield location, location_quantity, take
def _build_invoice_values_from_pickings(self, pickings): """ Build dict to create a new invoice from given pickings :param pickings: stock.picking recordset :return: dict """ picking = fields.first(pickings) partner_id = picking._get_partner_to_invoice() partner = self.env['res.partner'].browse(partner_id) inv_type = self._get_invoice_type() if inv_type in ('out_invoice', 'out_refund'): account_id = partner.property_account_receivable_id.id payment_term = partner.property_payment_term_id.id else: account_id = partner.property_account_payable_id.id payment_term = partner.property_supplier_payment_term_id.id company = self.env.user.company_id currency = company.currency_id if partner: code = picking.picking_type_id.code if partner.property_product_pricelist and code == 'outgoing': currency = partner.property_product_pricelist.currency_id journal = self._get_journal() invoice_obj = self.env['account.invoice'] values = invoice_obj.default_get(invoice_obj.fields_get().keys()) values.update({ 'origin': ", ".join(pickings.mapped("name")), 'user_id': self.env.user.id, 'partner_id': partner_id, 'account_id': account_id, 'payment_term_id': payment_term, 'type': inv_type, 'fiscal_position_id': partner.property_account_position_id.id, 'company_id': company.id, 'currency_id': currency.id, 'journal_id': journal.id, 'picking_ids': [(4, p.id, False) for p in pickings], }) invoice, values = self._simulate_invoice_onchange(values) return invoice, values
def _default_get_from_stock_package_level(self, res, ids): package_levels = self.env["stock.package_level"].browse(ids) # We keep only deliveries and receptions not canceled/done package_levels_to_keep = package_levels.filtered_domain([ ("state", "not in", ("done", "cancel")), ("picking_type_code", "=", "outgoing"), ]) res["package_level_ids"] = package_levels_to_keep.ids if not package_levels_to_keep: res["warning"] = _( "No package to load among selected ones (already done or " "not qualified as delivery).") elif package_levels != package_levels_to_keep: res["warning"] = _( "Packages to include have been updated, keeping only those " "qualified as delivery.") # Prefill the shipment with the planned one if any res["shipment_advice_id"] = fields.first( package_levels_to_keep.move_ids.shipment_advice_id or package_levels_to_keep.move_line_ids.move_id.shipment_advice_id).id return res
def _compute_new_invoice_quantity(self, invoice): self.ensure_one() if self.last: # For last install, let the system do the calc. return percent = self.percent for line in invoice.invoice_line_ids: assert len(line.sale_line_ids) >= 0, \ 'No matched order line for invoice line' order_line = fields.first(line.sale_line_ids) if order_line.is_downpayment: line.write({'quantity': -percent / 100}) # based on 1 unit else: plan_qty = order_line.product_uom_qty * (percent / 100) prec = order_line.product_uom.rounding if float_compare(plan_qty, line.quantity, prec) == 1: raise ValidationError( _('Plan quantity: %s, exceed invoiceable quantity: %s' '\nProduct should be delivered before invoice') % (plan_qty, line.quantity)) line.write({'quantity': plan_qty}) invoice.compute_taxes()
def write(self, values): dummy_doc = self.env.user.company_id.fiscal_dummy_id dummy_line = fields.first(dummy_doc.line_ids) if values.get("invoice_id"): values["document_id"] = ( self.env["account.invoice"] .browse(values["invoice_id"]) .fiscal_document_id.id ) result = super().write(values) for line in self: if line.wh_move_line_id and ( "quantity" in values or "price_unit" in values ): raise UserError( _("You can't edit one invoice related a withholding entry") ) if line.fiscal_document_line_id != dummy_line: shadowed_fiscal_vals = line._prepare_shadowed_fields_dict() line.fiscal_document_line_id.write(shadowed_fiscal_vals) return result
def test_purchase_last_price_info_new_order(self): purchase_order = self.purchase_model.create({ "date_order": "2000-01-01", "currency_id": self.currency_extra.id, "partner_id": self.partner.id, "order_line": [( 0, 0, { "product_id": self.product.id, "product_uom": self.product.uom_id.id, "price_unit": self.product.standard_price, "name": self.product.name, "date_planned": fields.Datetime.now(), "product_qty": 1, }, )], }) purchase_order.button_confirm() self.assertEqual( fields.Datetime.from_string(purchase_order.date_order).date(), fields.Datetime.from_string( self.product.last_purchase_date).date(), ) first_order_line = fields.first(purchase_order.order_line) self.assertEqual(first_order_line.price_unit, self.product.last_purchase_price) self.assertEqual( first_order_line.currency_id, self.product.last_purchase_currency_id, ) self.assertEqual(self.product.last_purchase_currency_id, self.currency_extra) self.assertEqual(self.product.last_purchase_price_currency, 2.0) self.assertEqual(self.partner, self.product.last_purchase_supplier_id) purchase_order.button_cancel() self.assertEqual(purchase_order.state, "cancel")
def set_lot_auto(self): """ Create lots using create_multi to avoid too much queries As move lines were created by product or by tracked 'serial' products, we apply the lot with both different approaches. """ values = [] production_lot_obj = self.env["stock.production.lot"] lots_by_product = dict() for line in self: values.append(line._prepare_auto_lot_values()) lots = production_lot_obj.create(values) for lot in lots: if lot.product_id.id not in lots_by_product: lots_by_product[lot.product_id.id] = lot else: lots_by_product[lot.product_id.id] += lot for line in self: lot = first(lots_by_product[line.product_id.id]) line.lot_id = lot if lot.product_id.tracking == "serial": lots_by_product[line.product_id.id] -= lot
def _update_assortment_items(self): """ Update the pricelist with current assortment: - Prepare values for new assorment items; - Delete previous items. - Create new assortments items; :return: bool """ self.ensure_one() if not self.assortment_filter_id.active: _logger.info( "The assortment item %s is ignored because the " "related assortment/filter is not active", self.display_name, ) return False ( products_to_add, products_to_update, products_to_remove, ) = self._get_assortment_changes() create_values, update_values = self._get_pricelist_item_values( products_to_add) old_items = self._get_related_items() update_items = old_items.filtered( lambda x: x.product_id in products_to_update) item = first(update_items) if self._check_need_update(item, update_values): update_items.write(update_values) self.env["product.pricelist.item"].with_user(SUPERUSER_ID).create( create_values) remove_items = old_items.filtered( lambda x: x.product_id in products_to_remove) remove_items.unlink() return True
def _create_partner(self, country, extra=None): data = self.data if extra: data.update(extra) data['country'] = {'id': self.env.ref('base.%s' % country).id} result = self.service.dispatch('create', params=data)['data'] partner = self.env['res.partner'].browse(result.get('id', False)) self.assertEqual(partner.email, data.get('email')) self.assertEqual(partner.shopinvader_bind_ids.external_id, data.get('external_id')) for key in data: if key == 'external_id': continue elif key == 'country': self.assertEqual(partner.country_id.id, data.get(key, {}).get('id', 't')) else: self.assertEqual(partner[key], data.get(key)) binded = first( partner.shopinvader_bind_ids.filtered( lambda s: s.backend_id.id == self.backend.id)) return binded
def _create_partner(self, country, extra=None): data = self.data if extra: data.update(extra) data["country"] = {"id": self.env.ref("base.%s" % country).id} result = self.service.dispatch("create", params=data)["data"] partner = self.env["res.partner"].browse(result.get("id", False)) self.assertEqual(partner.email, data.get("email")) self.assertEqual(partner.shopinvader_bind_ids.external_id, data.get("external_id")) for key in data: if key == "external_id": continue elif key == "country": self.assertEqual(partner.country_id.id, data.get(key, {}).get("id", "t")) else: self.assertEqual(partner[key], data.get(key)) binded = first( partner.shopinvader_bind_ids.filtered( lambda s: s.backend_id.id == self.backend.id)) return binded
def test_invoice_action_post_equipment_1(self): invoice = self._create_invoice() line_a = invoice.line_ids.filtered( lambda x: x.product_id == self.product_a) line_b = invoice.line_ids.filtered( lambda x: x.product_id == self.product_b) self.assertFalse(line_a.equipment_category_id) self.assertFalse(line_b.equipment_category_id) invoice.action_post() equipments = invoice.mapped("line_ids.equipment_ids") self.assertEqual(len(equipments), 2) self.assertTrue(line_a.equipment_category_id) self.assertEqual(len(line_a.equipment_ids), 2) self.assertEqual(len(line_b.equipment_ids), 0) equipment = fields.first(equipments) self.assertEqual(equipment.name, self.product_a.name) self.assertEqual(equipment.product_id, self.product_a) self.assertEqual(equipment.category_id.product_category_id, self.categ) self.assertEqual(equipment.assign_date, invoice.date) self.assertEqual(equipment.effective_date, invoice.date) self.assertEqual(equipment.partner_id, self.partner) self.assertEqual(equipment.partner_ref, invoice.ref)
def setUp(self): super(TestDatePlannedManual, self).setUp() self.po_model = self.env['purchase.order'] self.pol_model = self.env['purchase.order.line'] self.pp_model = self.env['product.product'] # Create some partner, products and supplier info: self.route_buy_id = self.env.ref('purchase_stock.route_warehouse0_buy') self.rule = first(self.route_buy_id.rule_ids) self.partner = self.env['res.partner'].create({ 'name': 'Test supplier', 'supplier': True, }) supplier_info_id = self.env['product.supplierinfo'].create({ 'name': self.partner.id, 'delay': 2, }).id self.product_1 = self.pp_model.create({ 'name': 'Test product 1', 'type': 'product', 'route_ids': [(4, self.route_buy_id.id)], 'seller_ids': [(4, supplier_info_id)], }) self.product_2 = self.pp_model.create({ 'name': 'Test product 2', 'type': 'product', }) # Create purchase order and dates self.purchase_order = self.po_model.create({ 'partner_id': self.partner.id, 'picking_type_id': self.rule.picking_type_id.id, }) self.next_week_time = datetime.now() + timedelta(days=7)