Esempio n. 1
0
    def get_shipment_cost_line(self, cost, unit_price=None):
        pool = Pool()
        SaleLine = pool.get('sale.line')

        product = self.carrier.carrier_product

        sequence = None
        if self.lines:
            last_line = self.lines[-1]
            if last_line.sequence is not None:
                sequence = last_line.sequence + 1

        shipment_cost = round_price(cost)
        cost_line = SaleLine(
            sale=self,
            sequence=sequence,
            type='line',
            product=product,
            quantity=1,  # XXX
            unit=product.sale_uom,
            shipment_cost=shipment_cost,
            )
        cost_line.on_change_product()
        if unit_price is not None:
            cost_line.unit_price = round_price(unit_price)
        else:
            cost_line.unit_price = round_price(cost)
        if not cost_line.unit_price:
            cost_line.quantity = 0
        cost_line.amount = cost_line.on_change_with_amount()
        return cost_line
Esempio n. 2
0
    def allocate_cost_by_value(self):
        pool = Pool()
        Currency = pool.get('currency.currency')
        Move = pool.get('stock.move')

        if not self.cost_used:
            return

        cost = Currency.compute(
            self.cost_currency_used, self.cost_used, self.company.currency,
            round=False)
        moves = [m for m in self.incoming_moves
            if m.state not in ('done', 'cancelled')]

        sum_value = 0
        unit_prices = {}
        for move in moves:
            unit_price = Currency.compute(move.currency, move.unit_price,
                self.company.currency, round=False)
            unit_prices[move.id] = unit_price
            sum_value += unit_price * Decimal(str(move.quantity))

        costs = []
        digit = Move.unit_price.digits[1]
        exp = Decimal(str(10.0 ** -digit))
        difference = cost
        for move in moves:
            quantity = Decimal(str(move.quantity))
            if not sum_value:
                move_cost = cost / Decimal(len(moves))
            else:
                move_cost = cost * quantity * unit_prices[move.id] / sum_value
            unit_shipment_cost = round_price(
                move_cost / quantity, rounding=ROUND_DOWN)
            costs.append({
                    'unit_shipment_cost': unit_shipment_cost,
                    'difference': move_cost - (unit_shipment_cost * quantity),
                    'move': move,
                    })
            difference -= unit_shipment_cost * quantity
        costs.sort(key=itemgetter('difference'))
        for cost in costs:
            move = cost['move']
            quantity = Decimal(str(move.quantity))
            if exp * quantity < difference:
                cost['unit_shipment_cost'] += exp
                difference -= exp * quantity
            if difference < exp:
                break

        for cost in costs:
            move = cost['move']
            unit_shipment_cost = Currency.compute(
                self.company.currency, cost['unit_shipment_cost'],
                move.currency, round=False)
            unit_shipment_cost = round_price(
                unit_shipment_cost, rounding=ROUND_HALF_EVEN)
            move.unit_price += unit_shipment_cost
            move.unit_shipment_cost = unit_shipment_cost
        Move.save(moves)
    def _allocate_cost(self, factors, sign=1):
        "Allocate cost on moves using factors"
        pool = Pool()
        Move = pool.get('stock.move')
        Currency = pool.get('currency.currency')
        assert sign in {1, -1}

        cost = self.cost
        currency = self.company.currency
        moves = [m for m in self.stock_moves() if m.quantity]

        costs = []
        digit = Move.unit_price.digits[1]
        exp = Decimal(str(10.0**-digit))
        difference = cost
        for move in moves:
            quantity = Decimal(str(move.quantity))
            move_cost = cost * factors[str(move.id)]
            unit_landed_cost = round_price(move_cost / quantity,
                                           rounding=ROUND_DOWN)
            costs.append({
                'unit_landed_cost':
                unit_landed_cost,
                'difference':
                move_cost - (unit_landed_cost * quantity),
                'move':
                move,
            })
            difference -= unit_landed_cost * quantity
        costs.sort(key=itemgetter('difference'), reverse=True)
        for cost in costs:
            move = cost['move']
            quantity = Decimal(str(move.quantity))
            if exp * quantity <= difference:
                cost['unit_landed_cost'] += exp
                difference -= exp * quantity
            if difference < exp:
                break

        for cost in costs:
            move = cost['move']
            with Transaction().set_context(date=move.effective_date):
                unit_landed_cost = Currency.compute(currency,
                                                    cost['unit_landed_cost'],
                                                    move.currency,
                                                    round=False)
            unit_landed_cost = round_price(unit_landed_cost,
                                           rounding=ROUND_HALF_EVEN)
            if move.unit_landed_cost is None:
                move.unit_landed_cost = 0
            move.unit_price += unit_landed_cost * sign
            move.unit_landed_cost += unit_landed_cost * sign
        Move.save(moves)
Esempio n. 4
0
 def on_change_discount_rate(self):
     if self.base_price is not None and self.discount_rate is not None:
         self.unit_price = round_price(self.base_price *
                                       (1 - self.discount_rate))
         self.discount_amount = self.on_change_with_discount_amount()
         self.discount = self.on_change_with_discount()
         self.amount = self.on_change_with_amount()
Esempio n. 5
0
    def get_cost_invoice_line(self, invoice):
        pool = Pool()
        Currency = pool.get('currency.currency')
        InvoiceLine = pool.get('account.invoice.line')

        if not self.cost_sale_used:
            return
        product = self.carrier.carrier_product

        invoice_line = InvoiceLine(invoice=invoice)
        invoice_line.on_change_invoice()
        invoice_line.type = 'line'
        invoice_line.quantity = 1  # XXX
        invoice_line.unit = product.sale_uom.id
        cost = self.cost_sale_used
        if invoice.currency != self.cost_sale_currency_used:
            with Transaction().set_context(date=invoice.currency_date):
                cost = Currency.compute(
                    self.cost_sale_currency_used, cost,
                    invoice.currency, round=False)
        invoice_line.unit_price = round_price(cost)
        invoice_line.product = product
        invoice_line.on_change_product()
        invoice_line.cost_shipments = [self]

        if not invoice_line.account:
            raise InvoiceShipmentCostError(
                gettext('sale_shipment_cost'
                    '.msg_shipment_cost_invoice_missing_account_revenue',
                    shipment=self.rec_name,
                    product=product.rec_name))
        return invoice_line
Esempio n. 6
0
    def recompute_cost_price(cls, products, start=None):
        pool = Pool()
        Move = pool.get('stock.move')
        costs = defaultdict(list)
        for product in products:
            if product.type == 'service':
                continue
            cost = getattr(
                product,
                'recompute_cost_price_%s' % product.cost_price_method)(start)
            cost = round_price(cost)
            costs[cost].append(product)

        updated = []
        for sub_products in grouped_slice(products):
            domain = [
                ('unit_price_updated', '=', True),
                cls._domain_moves_cost(),
                ('product', 'in', [p.id for p in sub_products]),
            ]
            if start:
                domain.append(('effective_date', '>=', start))
            updated += Move.search(domain, order=[])
        if updated:
            Move.write(updated, {'unit_price_updated': False})

        if costs:
            cls.update_cost_price(costs)
Esempio n. 7
0
    def get_cost(self, name):
        pool = Pool()
        Work = pool.get('production.work')
        Cycle = pool.get('production.work.cycle')
        table = self.__table__()
        work = Work.__table__()
        cycle = Cycle.__table__()
        cursor = Transaction().connection.cursor()

        cost = super(Production, self).get_cost(name)

        cursor.execute(
            *table.join(work, 'LEFT', condition=work.production == table.id
                        ).join(cycle, 'LEFT', condition=cycle.work ==
                               work.id).select(Sum(cycle.cost),
                                               where=(cycle.state == 'done')
                                               & (table.id == self.id)))
        cycle_cost, = cursor.fetchone()
        if cycle_cost is not None:
            # SQLite uses float for SUM
            if not isinstance(cycle_cost, Decimal):
                cycle_cost = Decimal(cycle_cost)
            cost += cycle_cost

        return round_price(cost)
Esempio n. 8
0
 def get_cost_price(self, product_cost_price=None):
     pool = Pool()
     SaleLine = pool.get('sale.line')
     Sale = pool.get('sale.sale')
     # For return sale's move use the cost price of the original sale
     if (isinstance(self.origin, SaleLine) and self.origin.quantity < 0
             and self.from_location.type != 'storage'
             and self.to_location.type == 'storage'
             and isinstance(self.origin.sale.origin, Sale)):
         sale = self.origin.sale.origin
         cost = Decimal(0)
         qty = Decimal(0)
         for move in sale.moves:
             if (move.state == 'done'
                     and move.from_location.type == 'storage'
                     and move.to_location.type == 'customer'
                     and move.product == self.product):
                 move_quantity = Decimal(str(move.internal_quantity))
                 cost_price = move.get_cost_price(
                     product_cost_price=move.cost_price)
                 qty += move_quantity
                 cost += cost_price * move_quantity
         if qty:
             product_cost_price = round_price(cost / qty)
     return super().get_cost_price(product_cost_price=product_cost_price)
Esempio n. 9
0
    def set_cost(cls, shipments):
        pool = Pool()
        Move = pool.get('stock.move')
        UoM = pool.get('product.uom')

        to_save = []
        for shipment in shipments:
            product_cost = defaultdict(int)
            s_product_qty = defaultdict(int)
            for s_move in shipment.supplier_moves:
                if s_move.state == 'cancelled':
                    continue
                internal_quantity = Decimal(str(s_move.internal_quantity))
                product_cost[s_move.product] += (s_move.get_cost_price() *
                                                 internal_quantity)

                quantity = UoM.compute_qty(s_move.uom,
                                           s_move.quantity,
                                           s_move.product.default_uom,
                                           round=False)
                s_product_qty[s_move.product] += quantity

            for product, cost in product_cost.items():
                qty = Decimal(str(s_product_qty[product]))
                if qty:
                    product_cost[product] = round_price(cost / qty)

            for c_move in shipment.customer_moves:
                cost_price = product_cost[c_move.product]
                if cost_price != c_move.cost_price:
                    c_move.cost_price = cost_price
                    to_save.append(c_move)
        if to_save:
            Move.save(to_save)
Esempio n. 10
0
    def on_change_product(self):
        pool = Pool()
        User = pool.get('res.user')
        ModelData = pool.get('ir.model.data')
        Uom = pool.get('product.uom')
        Currency = pool.get('currency.currency')

        if not self.product:
            return

        if self.price_list_hour:
            hour_uom = Uom(ModelData.get_id('product', 'uom_hour'))
            self.list_price = Uom.compute_price(self.product.default_uom,
                                                self.product.list_price,
                                                hour_uom)
        else:
            self.list_price = self.product.list_price

        if self.company:
            user = User(Transaction().user)
            if user.company != self.company:
                if user.company.currency != self.company.currency:
                    self.list_price = Currency.compute(user.company.currency,
                                                       self.list_price,
                                                       self.company.currency,
                                                       round=False)

        self.list_price = round_price(self.list_price)
Esempio n. 11
0
    def on_change_service(self):
        pool = Pool()
        Product = pool.get('product.product')

        if not self.service:
            self.consumption_recurrence = None
            self.consumption_delay = None
            return

        party = None
        party_context = {}
        if self.subscription and self.subscription.party:
            party = self.subscription.party
            if party.lang:
                party_context['language'] = party.lang.code

        product = self.service.product
        category = product.sale_uom.category
        if not self.unit or self.unit.category != category:
            self.unit = product.sale_uom
            self.unit_digits = product.sale_uom.digits

        with Transaction().set_context(self._get_context_sale_price()):
            self.unit_price = Product.get_sale_price([product], self.quantity
                                                     or 0)[product.id]
            if self.unit_price:
                self.unit_price = round_price(self.unit_price)

        self.consumption_recurrence = self.service.consumption_recurrence
        self.consumption_delay = self.service.consumption_delay
Esempio n. 12
0
 def test_round_price(self):
     for value, result in [
             (Decimal('1'), Decimal('1.0000')),
             (Decimal('1.12345'), Decimal('1.1234')),
             (1, Decimal('1')),
             ]:
         with self.subTest(value=value):
             self.assertEqual(round_price(value), result)
Esempio n. 13
0
 def get_move(self, move_type):
     pool = Pool()
     Currency = pool.get('currency.currency')
     move = super().get_move(move_type)
     if move and self.purchase.customer:
         cost_price = Currency.compute(move.currency, move.unit_price,
                                       move.company.currency)
         move.cost_price = round_price(cost_price)
     return move
Esempio n. 14
0
 def get_cost(self, name):
     cost = Decimal(0)
     for input_ in self.inputs:
         if input_.cost_price is not None:
             cost_price = input_.cost_price
         else:
             cost_price = input_.product.cost_price
         cost += (Decimal(str(input_.internal_quantity)) * cost_price)
     return round_price(cost)
Esempio n. 15
0
 def compute_base_price(self):
     pool = Pool()
     Uom = pool.get('product.uom')
     if self.product and self.product.list_price is not None:
         price = self.product.list_price
         if self.unit:
             price = Uom.compute_price(self.product.default_uom, price,
                                       self.unit)
         return round_price(price)
Esempio n. 16
0
 def get_cost(self, name):
     pool = Pool()
     Currency = pool.get('currency.currency')
     cost = super(Production, self).get_cost(name)
     for line in self.purchase_lines:
         if line.purchase.state != 'cancelled':
             cost += Currency.compute(
                 line.purchase.currency, line.amount,
                 self.company.currency, round=False)
     return round_price(cost)
Esempio n. 17
0
        def compute_fifo_cost_price(quantity, date):
            fifo_moves = self.get_fifo_move(float(quantity), date=date)

            cost_price = Decimal(0)
            consumed_qty = 0
            for move, move_qty in fifo_moves:
                consumed_qty += move_qty
                cost_price += move.get_cost_price() * Decimal(str(move_qty))
            if consumed_qty:
                return round_price(cost_price / Decimal(str(consumed_qty)))
Esempio n. 18
0
 def _compute_costs(self):
     costs = {
         'cost': None,
         'cost_currency': None,
         }
     if self.carrier:
         with Transaction().set_context(self._get_carrier_context()):
             cost, currency_id = self.carrier.get_purchase_price()
         if cost is not None:
             costs['cost'] = round_price(cost)
             costs['cost_currency'] = currency_id
     return costs
Esempio n. 19
0
    def _get_supplier_invoice_line_consignment(self):
        pool = Pool()
        InvoiceLine = pool.get('account.invoice.line')
        Product = pool.get('product.product')
        ProductSupplier = pool.get('purchase.product_supplier')

        with Transaction().set_context(
                supplier=self.from_location.consignment_party.id):
            pattern = ProductSupplier.get_pattern()
        for product_supplier in self.product.product_suppliers_used(**pattern):
            currency = product_supplier.currency
            break
        else:
            currency = self.company.currency

        line = InvoiceLine()
        line.invoice_type = 'in'
        line.type = 'line'
        line.company = self.company
        line.party = self.from_location.consignment_party
        line.currency = currency
        line.product = self.product
        line.description = self.product.name
        line.quantity = self.quantity
        line.unit = self.uom
        line.account = self.product.account_expense_used
        line.stock_moves = [self]
        line.origin = self

        taxes = []
        pattern = self._get_tax_rule_pattern()
        for tax in line.product.supplier_taxes_used:
            if line.party.supplier_tax_rule:
                tax_ids = line.party.supplier_tax_rule.apply(tax, pattern)
                if tax_ids:
                    taxes.extend(tax_ids)
                continue
            taxes.append(tax.id)
        if line.party.supplier_tax_rule:
            tax_ids = line.party.supplier_tax_rule.apply(None, pattern)
            if tax_ids:
                taxes.extend(tax_ids)
        line.taxes = taxes

        with Transaction().set_context(currency=line.currency.id,
                                       supplier=line.party.id,
                                       uom=line.unit,
                                       taxes=[t.id for t in line.taxes]):
            line.unit_price = Product.get_purchase_price(
                [line.product], line.quantity)[line.product.id]
            if line.unit_price is not None:
                line.unit_price = round_price(line.unit_price)
        return line
Esempio n. 20
0
 def on_change_secondary_unit_price(self, name=None):
     pool = Pool()
     Uom = pool.get('product.uom')
     if (self.secondary_unit_price is not None and self.secondary_unit
             and self.unit
             and (self.secondary_uom_factor or self.secondary_uom_rate)):
         self.unit_price = Uom.compute_price(self.secondary_unit,
                                             self.secondary_unit_price,
                                             self.unit,
                                             factor=self.secondary_uom_rate,
                                             rate=self.secondary_uom_factor)
         self.unit_price = round_price(self.unit_price)
         self.amount = self.on_change_with_amount()
Esempio n. 21
0
 def _compute_costs(self):
     costs = super()._compute_costs()
     costs.update({
             'cost_sale': None,
             'cost_sale_currency': None,
             })
     if self.carrier and self.cost_method == 'shipment':
         with Transaction().set_context(self._get_carrier_context()):
             cost_sale, sale_currency_id = self.carrier.get_sale_price()
         if cost_sale is not None:
             costs['cost_sale'] = round_price(cost_sale)
         costs['cost_sale_currency'] = sale_currency_id
     return costs
Esempio n. 22
0
 def on_change_with_secondary_unit_price(self, name=None):
     pool = Pool()
     Uom = pool.get('product.uom')
     if (self.unit_price is not None and self.unit and self.secondary_unit
             and (self.secondary_uom_factor or self.secondary_uom_rate)):
         unit_price = Uom.compute_price(self.unit,
                                        self.unit_price,
                                        self.secondary_unit,
                                        factor=self.secondary_uom_factor,
                                        rate=self.secondary_uom_rate)
         return round_price(unit_price)
     else:
         return None
Esempio n. 23
0
 def _allocate_shipment_cost(self, cost, factors):
     moves = []
     for move in self.shipment_cost_moves:
         ratio = factors[move.id]
         quantity = Decimal(str(move.internal_quantity))
         if quantity and ratio:
             cost_price = round_price(cost * ratio / quantity)
         else:
             cost_price = Decimal(0)
         if move.shipment_out_cost_price != cost_price:
             move.shipment_out_cost_price = cost_price
             moves.append(move)
     return moves
Esempio n. 24
0
 def get_multivalue(self, name, **pattern):
     pool = Pool()
     Uom = pool.get('product.uom')
     value = super().get_multivalue(name, **pattern)
     if name == 'cost_price' and self.type == 'kit':
         value = Decimal(0)
         for component in self.components_used:
             cost_price = component.product.get_multivalue(
                 'cost_price', **pattern)
             cost_price = Uom.compute_price(
                 component.product.default_uom, cost_price, component.unit)
             value += cost_price * Decimal(str(component.quantity))
         value = round_price(value)
     return value
Esempio n. 25
0
    def _update_fifo_out_product_cost_price(self):
        '''
        Update the product cost price of the given product on the move. Update
        fifo_quantity on the concerned incomming moves. Return the
        cost price for outputing the given product and quantity.
        '''
        pool = Pool()
        Uom = pool.get('product.uom')

        total_qty = Uom.compute_qty(self.uom,
                                    self.quantity,
                                    self.product.default_uom,
                                    round=False)

        with Transaction().set_context(company=self.company.id):
            fifo_moves = self.product.get_fifo_move(total_qty)

        cost_price = Decimal("0.0")
        consumed_qty = 0.0
        to_save = []
        for move, move_qty in fifo_moves:
            consumed_qty += move_qty
            cost_price += move.get_cost_price() * Decimal(str(move_qty))

            move_qty = Uom.compute_qty(self.product.default_uom,
                                       move_qty,
                                       move.uom,
                                       round=False)
            move.fifo_quantity = (move.fifo_quantity or 0.0) + move_qty
            # Due to float, the fifo quantity result can exceed the quantity.
            assert move.quantity >= move.fifo_quantity - move.uom.rounding
            move.fifo_quantity = min(move.fifo_quantity, move.quantity)
            to_save.append(move)

        if consumed_qty:
            cost_price = cost_price / Decimal(str(consumed_qty))
        else:
            cost_price = self.product.get_multivalue(
                'cost_price', **self._cost_price_pattern)

        # Compute average cost price
        average_cost_price = self._compute_product_cost_price(
            'out', product_cost_price=cost_price)

        if cost_price:
            cost_price = round_price(cost_price)
        else:
            cost_price = average_cost_price
        return cost_price, average_cost_price, to_save
Esempio n. 26
0
 def _compute_costs(self):
     pool = Pool()
     Currency = pool.get('currency.currency')
     costs = {
         'cost': None,
     }
     if self.carrier:
         with Transaction().set_context(self._get_carrier_context()):
             cost, currency_id = self.carrier.get_purchase_price()
         if cost is not None:
             cost = Currency.compute(Currency(currency_id),
                                     cost,
                                     self.company.currency,
                                     round=False)
             costs['cost'] = round_price(cost)
     return costs
Esempio n. 27
0
 def _compute_component_unit_price(self, unit_price):
     pool = Pool()
     Currency = pool.get('currency.currency')
     UoM = pool.get('product.uom')
     amount, quantity = 0, 0
     for line in self.origin.line.invoice_lines:
         if line.invoice and line.invoice.state in {'posted', 'paid'}:
             with Transaction().set_context(date=self.effective_date):
                 amount += Currency.compute(line.invoice.currency,
                                            line.amount, self.currency)
             quantity += UoM.compute_qty(line.unit, line.quantity,
                                         self.origin.line.unit)
     amount *= self.origin.price_ratio
     if quantity:
         unit_price = round_price(amount / Decimal(str(quantity)))
     unit_price = UoM.compute_price(self.origin.unit, unit_price, self.uom)
     return unit_price
Esempio n. 28
0
    def compute_purchase_line(cls, key, requests, purchase):
        pool = Pool()
        RequisitionLine = pool.get('purchase.requisition.line')
        Uom = pool.get('product.uom')

        line = super().compute_purchase_line(key, requests, purchase)

        key_values = dict(key)
        if (key_values.get('unit_price') is not None
                and any(
                    isinstance(r.origin, RequisitionLine) for r in requests)):
            line.unit_price = round_price(
                Uom.compute_price(
                    key_values.get('unit', line.unit),
                    key_values['unit_price'],
                    line.unit))
        return line
Esempio n. 29
0
 def apply(self, sale):
     applied = False
     for line in sale.lines:
         if line.type != 'line':
             continue
         if not self.is_valid_sale_line(line):
             continue
         context = self.get_context_formula(line)
         new_price = self.get_unit_price(**context)
         if new_price is not None:
             if new_price < 0:
                 new_price = Decimal(0)
             if line.unit_price >= new_price:
                 line.unit_price = round_price(new_price)
                 line.promotion = self
                 applied = True
     if applied:
         sale.lines = sale.lines  # Trigger the change
Esempio n. 30
0
 def get_cost_value(cls, products, name):
     cost_values = {p.id: None for p in products}
     context = {}
     trans_context = Transaction().context
     if trans_context.get('stock_date_end'):
         # Use the last cost_price of the day
         context['_datetime'] = datetime.datetime.combine(
             trans_context['stock_date_end'], datetime.time.max)
         # The date could be before the product creation
         products = [
             p for p in products if p.create_date <= context['_datetime']
         ]
     with Transaction().set_context(context):
         for product in cls.browse(products):
             # The product may not have a cost price
             if product.cost_price is not None:
                 cost_values[product.id] = round_price(
                     Decimal(str(product.quantity)) * product.cost_price)
     return cost_values