Example #1
0
    def _onchange_product_id_check_availability(self):
        if not self.product_id or not self.product_uom_qty or not self.product_uom:
            self.product_packaging = False
            return {}
        if self.product_id.type == 'product':
            precision = self.env['decimal.precision'].precision_get(
                'Product Unit of Measure')
            product = self.product_id.with_context(
                warehouse=self.order_id.warehouse_id.id,
                lang=self.order_id.partner_id.lang or self.env.user.lang
                or 'en_US')
            product_qty = self.product_uom._compute_quantity(
                self.product_uom_qty, self.product_id.uom_id)
            if float_compare(product.virtual_available,
                             product_qty,
                             precision_digits=precision) == -1:
                is_available = self._check_routing()
                if not is_available:
                    message =  _('You plan to sell %s %s but you only have %s %s available in %s warehouse.') % \
                            (self.product_uom_qty, self.product_uom.name, product.virtual_available, product.uom_id.name, self.order_id.warehouse_id.name)
                    # We check if some products are available in other warehouses.
                    if float_compare(product.virtual_available,
                                     self.product_id.virtual_available,
                                     precision_digits=precision) == -1:
                        message += _('\nThere are %s %s available accross all warehouses.') % \
                                (self.product_id.virtual_available, product.uom_id.name)

                    warning_mess = {
                        'title': _('Not enough inventory!'),
                        'message': message
                    }
                    return {'warning': warning_mess}
        return {}
Example #2
0
 def _check_holidays(self):
     for holiday in self:
         if holiday.holiday_type != 'employee' or holiday.type != 'remove' or not holiday.employee_id or holiday.holiday_status_id.limit:
             continue
         leave_days = holiday.holiday_status_id.get_days(
             holiday.employee_id.id)[holiday.holiday_status_id.id]
         if float_compare(leave_days['remaining_leaves'], 0, precision_digits=2) == -1 or \
           float_compare(leave_days['virtual_remaining_leaves'], 0, precision_digits=2) == -1:
             raise ValidationError(
                 _('The number of remaining leaves is not sufficient for this leave type.\n'
                   'Please verify also the leaves waiting for validation.'))
Example #3
0
 def do_produce(self):
     # Nothing to do for lots since values are created using default data (stock.move.lots)
     quantity = self.product_qty
     if float_compare(quantity, 0, precision_rounding=self.product_uom_id.rounding) <= 0:
         raise UserError(_("The production order for '%s' has no quantity specified") % self.product_id.display_name)
     for move in self.production_id.move_raw_ids:
         # TODO currently not possible to guess if the user updated quantity by hand or automatically by the produce wizard.
         if move.product_id.tracking == 'none' and move.state not in ('done', 'cancel') and move.unit_factor:
             rounding = move.product_uom.rounding
             if self.product_id.tracking != 'none':
                 qty_to_add = float_round(quantity * move.unit_factor, precision_rounding=rounding)
                 move._generate_consumed_move_line(qty_to_add, self.lot_id)
             elif len(move._get_move_lines()) < 2:
                 move.quantity_done += float_round(quantity * move.unit_factor, precision_rounding=rounding)
             else:
                 move._set_quantity_done(quantity * move.unit_factor)
     for move in self.production_id.move_finished_ids:
         if move.product_id.tracking == 'none' and move.state not in ('done', 'cancel'):
             rounding = move.product_uom.rounding
             if move.product_id.id == self.production_id.product_id.id:
                 move.quantity_done += float_round(quantity, precision_rounding=rounding)
             elif move.unit_factor:
                 # byproducts handling
                 move.quantity_done += float_round(quantity * move.unit_factor, precision_rounding=rounding)
     self.check_finished_move_lots()
     if self.production_id.state == 'confirmed':
         self.production_id.write({
             'state': 'progress',
             'date_start': datetime.now(),
         })
     return {'type': 'ir.actions.act_window_close'}
    def _check_sum(self):
        """ Check if each cost line its valuation lines sum to the correct amount
        and if the overall total amount is correct also """
        prec_digits = self.env['decimal.precision'].precision_get('Account')
        for landed_cost in self:
            total_amount = sum(landed_cost.valuation_adjustment_lines.mapped('additional_landed_cost'))
            if not tools.float_compare(total_amount, landed_cost.amount_total, precision_digits=prec_digits) == 0:
                return False

            val_to_cost_lines = defaultdict(lambda: 0.0)
            for val_line in landed_cost.valuation_adjustment_lines:
                val_to_cost_lines[val_line.cost_line_id] += val_line.additional_landed_cost
            if any(tools.float_compare(cost_line.price_unit, val_amount, precision_digits=prec_digits) != 0
                   for cost_line, val_amount in val_to_cost_lines.items()):
                return False
        return True
Example #5
0
 def action_validate(self):
     self.ensure_one()
     if self.product_id.type != 'product':
         return self.do_scrap()
     precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
     available_qty = sum(self.env['stock.quant']._gather(self.product_id,
                                                         self.location_id,
                                                         self.lot_id,
                                                         self.package_id,
                                                         self.owner_id,
                                                         strict=True).mapped('quantity'))
     if float_compare(available_qty, self.scrap_qty, precision_digits=precision) >= 0:
         return self.do_scrap()
     else:
         return {
             'name': _('Insufficient Quantity'),
             'view_type': 'form',
             'view_mode': 'form',
             'res_model': 'stock.warn.insufficient.qty.scrap',
             'view_id': self.env.ref('stock.stock_warn_insufficient_qty_scrap_form_view').id,
             'type': 'ir.actions.act_window',
             'context': {
                 'default_product_id': self.product_id.id,
                 'default_location_id': self.location_id.id,
                 'default_scrap_id': self.id
             },
             'target': 'new'
         }
Example #6
0
 def _compute_consumed_less_than_planned(self):
     for order in self:
         order.consumed_less_than_planned = any(order.move_raw_ids.filtered(
             lambda move: float_compare(move.quantity_done,
                                        move.product_uom_qty,
                                        precision_rounding=move.product_uom.rounding) == -1)
         )
Example #7
0
 def action_validate(self):
     self.ensure_one()
     precision = self.env['decimal.precision'].precision_get(
         'Product Unit of Measure')
     available_qty = self.env['stock.quant']._get_available_quantity(
         self.product_id, self.location_id, self.lot_id, strict=True)
     if float_compare(available_qty,
                      self.product_qty,
                      precision_digits=precision) >= 0:
         return self.action_unbuild()
     else:
         return {
             'name':
             _('Insufficient Quantity'),
             'view_type':
             'form',
             'view_mode':
             'form',
             'res_model':
             'stock.warn.insufficient.qty.unbuild',
             'view_id':
             self.env.ref(
                 'mrp.stock_warn_insufficient_qty_unbuild_form_view').id,
             'type':
             'ir.actions.act_window',
             'context': {
                 'default_product_id': self.product_id.id,
                 'default_location_id': self.location_id.id,
                 'default_unbuild_id': self.id
             },
             'target':
             'new'
         }
Example #8
0
    def _confirm_so(self):
        """ Check tx state, confirm the potential SO """
        self.ensure_one()
        if self.sale_order_id.state not in ['draft', 'sent', 'sale']:
            _logger.warning(
                '<%s> transaction STATE INCORRECT for order %s (ID %s, state %s)',
                self.acquirer_id.provider, self.sale_order_id.name,
                self.sale_order_id.id, self.sale_order_id.state)
            return 'pay_sale_invalid_doc_state'
        if not float_compare(self.amount, self.sale_order_id.amount_total,
                             2) == 0:
            _logger.warning(
                '<%s> transaction AMOUNT MISMATCH for order %s (ID %s): expected %r, got %r',
                self.acquirer_id.provider,
                self.sale_order_id.name,
                self.sale_order_id.id,
                self.sale_order_id.amount_total,
                self.amount,
            )
            self.sale_order_id.message_post(
                subject=_("Amount Mismatch (%s)") % self.acquirer_id.provider,
                body=
                _("The sale order was not confirmed despite response from the acquirer (%s): SO amount is %r but acquirer replied with %r."
                  ) % (
                      self.acquirer_id.provider,
                      self.sale_order_id.amount_total,
                      self.amount,
                  ))
            return 'pay_sale_tx_amount'

        if self.state == 'authorized' and self.acquirer_id.capture_manually:
            _logger.info(
                '<%s> transaction authorized, auto-confirming order %s (ID %s)',
                self.acquirer_id.provider, self.sale_order_id.name,
                self.sale_order_id.id)
            if self.sale_order_id.state in ('draft', 'sent'):
                self.sale_order_id.with_context(
                    send_email=True).action_confirm()
        elif self.state == 'done':
            _logger.info(
                '<%s> transaction completed, auto-confirming order %s (ID %s)',
                self.acquirer_id.provider, self.sale_order_id.name,
                self.sale_order_id.id)
            if self.sale_order_id.state in ('draft', 'sent'):
                self.sale_order_id.with_context(
                    send_email=True).action_confirm()
        elif self.state not in ['cancel', 'error'
                                ] and self.sale_order_id.state == 'draft':
            _logger.info(
                '<%s> transaction pending/to confirm manually, sending quote email for order %s (ID %s)',
                self.acquirer_id.provider, self.sale_order_id.name,
                self.sale_order_id.id)
            self.sale_order_id.force_quotation_send()
        else:
            _logger.warning('<%s> transaction MISMATCH for order %s (ID %s)',
                            self.acquirer_id.provider, self.sale_order_id.name,
                            self.sale_order_id.id)
            return 'pay_sale_tx_state'
        return True
Example #9
0
 def _check_main_currency_rounding(self):
     if any(precision.name == 'Account' and tools.float_compare(
             self.env.user.company_id.currency_id.rounding,
             10**-precision.digits,
             precision_digits=6) == -1 for precision in self):
         raise ValidationError(
             _("You cannot define the decimal precision of 'Account' as greater than the rounding factor of the company's main currency"
               ))
     return True
 def reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False):
     res = super(AccountMoveLine, self).reconcile(writeoff_acc_id=writeoff_acc_id, writeoff_journal_id=writeoff_journal_id)
     account_move_ids = [l.move_id.id for l in self if float_compare(l.move_id.matched_percentage, 1, precision_digits=5) == 0]
     if account_move_ids:
         expense_sheets = self.env['hr.expense.sheet'].search([
             ('account_move_id', 'in', account_move_ids), ('state', '!=', 'done')
         ])
         expense_sheets.set_to_paid()
     return res
Example #11
0
 def _onchange_qty_done(self):
     """ When the user is encoding a produce line for a tracked product, we apply some logic to
     help him. This onchange will warn him if he set `qty_done` to a non-supported value.
     """
     res = {}
     if self.product_id.tracking == 'serial':
         if float_compare(self.qty_done, 1.0, precision_rounding=self.move_id.product_id.uom_id.rounding) != 0:
             message = _('You can only process 1.0 %s for products with unique serial number.') % self.product_id.uom_id.name
             res['warning'] = {'title': _('Warning'), 'message': message}
     return res
Example #12
0
    def _confirm_invoice(self):
        """ Check tx state, confirm and pay potential invoice """
        self.ensure_one()
        # check tx state, confirm the potential SO
        if self.account_invoice_id.state != 'open':
            _logger.warning(
                '<%s> transaction STATE INCORRECT for invoice %s (ID %s, state %s)',
                self.acquirer_id.provider, self.account_invoice_id.number,
                self.account_invoice_id.id, self.account_invoice_id.state)
            return 'pay_invoice_invalid_doc_state'
        if not float_compare(self.amount, self.account_invoice_id.amount_total,
                             2) == 0:
            _logger.warning(
                '<%s> transaction AMOUNT MISMATCH for invoice %s (ID %s): expected %r, got %r',
                self.acquirer_id.provider,
                self.account_invoice_id.number,
                self.account_invoice_id.id,
                self.account_invoice_id.amount_total,
                self.amount,
            )
            self.account_invoice_id.message_post(
                subject=_("Amount Mismatch (%s)") % self.acquirer_id.provider,
                body=
                _("The invoice was not confirmed despite response from the acquirer (%s): invoice amount is %r but acquirer replied with %r."
                  ) % (
                      self.acquirer_id.provider,
                      self.account_invoice_id.amount_total,
                      self.amount,
                  ))
            return 'pay_invoice_tx_amount'

        if self.state == 'authorized' and self.acquirer_id.capture_manually:
            _logger.info(
                '<%s> transaction authorized, nothing to do with invoice %s (ID %s)',
                self.acquirer_id.provider, self.account_invoice_id.number,
                self.account_invoice_id.id)
        elif self.state == 'done':
            _logger.info(
                '<%s> transaction completed, paying invoice %s (ID %s)',
                self.acquirer_id.provider, self.account_invoice_id.number,
                self.account_invoice_id.id)
            self._pay_invoice()
        else:
            _logger.warning('<%s> transaction MISMATCH for invoice %s (ID %s)',
                            self.acquirer_id.provider,
                            self.account_invoice_id.number,
                            self.account_invoice_id.id)
            return 'pay_invoice_tx_state'
        return True
Example #13
0
 def _update_line_quantity(self, values):
     precision = self.env['decimal.precision'].precision_get(
         'Product Unit of Measure')
     if self.mapped('qty_delivered') and float_compare(
             values['product_uom_qty'],
             max(self.mapped('qty_delivered')),
             precision_digits=precision) == -1:
         raise UserError(
             'You cannot decrease the ordered quantity below the delivered quantity.\n'
             'Create a return first.')
     for line in self:
         pickings = line.order_id.picking_ids.filtered(
             lambda p: p.state not in ('done', 'cancel'))
         for picking in pickings:
             picking.message_post(
                 "The quantity of %s has been updated from %d to %d in %s" %
                 (line.product_id.display_name, line.product_uom_qty,
                  values['product_uom_qty'], line.order_id.name))
     super(SaleOrderLine, self)._update_line_quantity(values)
Example #14
0
 def write(self, values):
     lines = self.env['sale.order.line']
     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)
     previous_product_uom_qty = {
         line.id: line.product_uom_qty
         for line in lines
     }
     res = super(SaleOrderLine, self).write(values)
     if lines:
         lines.with_context(
             previous_product_uom_qty=previous_product_uom_qty
         )._action_launch_procurement_rule()
     return res
Example #15
0
    def _select_seller(self,
                       partner_id=False,
                       quantity=0.0,
                       date=None,
                       uom_id=False):
        self.ensure_one()
        if date is None:
            date = fields.Date.context_today(self)
        precision = self.env['decimal.precision'].precision_get(
            'Product Unit of Measure')

        res = self.env['product.supplierinfo']
        for seller in self.seller_ids:
            # Set quantity in UoM of seller
            quantity_uom_seller = quantity
            if quantity_uom_seller and uom_id and uom_id != seller.product_uom:
                quantity_uom_seller = uom_id._compute_quantity(
                    quantity_uom_seller, seller.product_uom)

            if seller.date_start and seller.date_start > date:
                continue
            if seller.date_end and seller.date_end < date:
                continue
            if partner_id and seller.name not in [
                    partner_id, partner_id.parent_id
            ]:
                continue
            if float_compare(quantity_uom_seller,
                             seller.min_qty,
                             precision_digits=precision) == -1:
                continue
            if seller.product_id and seller.product_id != self:
                continue

            res |= seller
            break
        return res
Example #16
0
    def default_get(self, fields):
        res = super(MrpProductProduce, self).default_get(fields)
        if self._context and self._context.get('active_id'):
            production = self.env['mrp.production'].browse(self._context['active_id'])
            serial_finished = (production.product_id.tracking == 'serial')
            if serial_finished:
                todo_quantity = 1.0
            else:
                main_product_moves = production.move_finished_ids.filtered(lambda x: x.product_id.id == production.product_id.id)
                todo_quantity = production.product_qty - sum(main_product_moves.mapped('quantity_done'))
                todo_quantity = todo_quantity if (todo_quantity > 0) else 0
            if 'production_id' in fields:
                res['production_id'] = production.id
            if 'product_id' in fields:
                res['product_id'] = production.product_id.id
            if 'product_uom_id' in fields:
                res['product_uom_id'] = production.product_uom_id.id
            if 'serial' in fields:
                res['serial'] = bool(serial_finished)
            if 'product_qty' in fields:
                res['product_qty'] = todo_quantity
            if 'produce_line_ids' in fields:
                lines = []
                for move in production.move_raw_ids.filtered(lambda x: (x.product_id.tracking != 'none') and x.state not in ('done', 'cancel') and x.bom_line_id):
                    qty_to_consume = float_round(todo_quantity / move.bom_line_id.bom_id.product_qty * move.bom_line_id.product_qty,
                                                 precision_rounding=move.product_uom.rounding, rounding_method="UP")
                    for move_line in move.move_line_ids:
                        if float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) <= 0:
                            break
                        if move_line.lot_produced_id or float_compare(move_line.product_uom_qty, move_line.qty_done, precision_rounding=move.product_uom.rounding) <= 0:
                            continue
                        to_consume_in_line = min(qty_to_consume, move_line.product_uom_qty)
                        lines.append({
                            'move_id': move.id,
                            'qty_to_consume': to_consume_in_line,
                            'qty_done': 0.0,
                            'lot_id': move_line.lot_id.id,
                            'product_uom_id': move.product_uom.id,
                            'product_id': move.product_id.id,
                        })
                        qty_to_consume -= to_consume_in_line
                    if float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) > 0:
                        if move.product_id.tracking == 'serial':
                            while float_compare(qty_to_consume, 0.0, precision_rounding=move.product_uom.rounding) > 0:
                                lines.append({
                                    'move_id': move.id,
                                    'qty_to_consume': 1,
                                    'qty_done': 0.0,
                                    'product_uom_id': move.product_uom.id,
                                    'product_id': move.product_id.id,
                                })
                                qty_to_consume -= 1
                        else:
                            lines.append({
                                'move_id': move.id,
                                'qty_to_consume': qty_to_consume,
                                'qty_done': 0.0,
                                'product_uom_id': move.product_uom.id,
                                'product_id': move.product_id.id,
                            })

                res['produce_line_ids'] = [(0, 0, x) for x in lines]
        return res
Example #17
0
    def _procure_orderpoint_confirm(self,
                                    use_new_cursor=False,
                                    company_id=False):
        """ Create procurements based on orderpoints.
        :param bool use_new_cursor: if set, use a dedicated cursor and auto-commit after processing
            1000 orderpoints.
            This is appropriate for batch jobs only.
        """
        if company_id and self.env.user.company_id.id != company_id:
            # To ensure that the company_id is taken into account for
            # all the processes triggered by this method
            # i.e. If a PO is generated by the run of the procurements the
            # sequence to use is the one for the specified company not the
            # one of the user's company
            self = self.with_context(company_id=company_id,
                                     force_company=company_id)
        OrderPoint = self.env['stock.warehouse.orderpoint']
        domain = self._get_orderpoint_domain(company_id=company_id)
        orderpoints_noprefetch = OrderPoint.with_context(
            prefetch_fields=False).search(
                domain,
                order=self._procurement_from_orderpoint_get_order()).ids
        while orderpoints_noprefetch:
            if use_new_cursor:
                cr = registry(self._cr.dbname).cursor()
                self = self.with_env(self.env(cr=cr))
            OrderPoint = self.env['stock.warehouse.orderpoint']

            orderpoints = OrderPoint.browse(orderpoints_noprefetch[:1000])
            orderpoints_noprefetch = orderpoints_noprefetch[1000:]

            # Calculate groups that can be executed together
            location_data = OrderedDict()

            def makedefault():
                return {
                    'products': self.env['product.product'],
                    'orderpoints': self.env['stock.warehouse.orderpoint'],
                    'groups': []
                }

            for orderpoint in orderpoints:
                key = self._procurement_from_orderpoint_get_grouping_key(
                    [orderpoint.id])
                if not location_data.get(key):
                    location_data[key] = makedefault()
                location_data[key]['products'] += orderpoint.product_id
                location_data[key]['orderpoints'] += orderpoint
                location_data[key][
                    'groups'] = self._procurement_from_orderpoint_get_groups(
                        [orderpoint.id])

            for location_id, location_data in location_data.items():
                location_orderpoints = location_data['orderpoints']
                product_context = dict(
                    self._context,
                    location=location_orderpoints[0].location_id.id)
                substract_quantity = location_orderpoints._quantity_in_progress(
                )

                for group in location_data['groups']:
                    if group.get('from_date'):
                        product_context['from_date'] = group[
                            'from_date'].strftime(
                                DEFAULT_SERVER_DATETIME_FORMAT)
                    if group['to_date']:
                        product_context['to_date'] = group['to_date'].strftime(
                            DEFAULT_SERVER_DATETIME_FORMAT)
                    product_quantity = location_data['products'].with_context(
                        product_context)._product_available()
                    for orderpoint in location_orderpoints:
                        try:
                            op_product_virtual = product_quantity[
                                orderpoint.product_id.id]['virtual_available']
                            if op_product_virtual is None:
                                continue
                            if float_compare(op_product_virtual,
                                             orderpoint.product_min_qty,
                                             precision_rounding=orderpoint.
                                             product_uom.rounding) <= 0:
                                qty = max(orderpoint.product_min_qty,
                                          orderpoint.product_max_qty
                                          ) - op_product_virtual
                                remainder = orderpoint.qty_multiple > 0 and qty % orderpoint.qty_multiple or 0.0

                                if float_compare(remainder,
                                                 0.0,
                                                 precision_rounding=orderpoint.
                                                 product_uom.rounding) > 0:
                                    qty += orderpoint.qty_multiple - remainder

                                if float_compare(qty,
                                                 0.0,
                                                 precision_rounding=orderpoint.
                                                 product_uom.rounding) < 0:
                                    continue

                                qty -= substract_quantity[orderpoint.id]
                                qty_rounded = float_round(
                                    qty,
                                    precision_rounding=orderpoint.product_uom.
                                    rounding)
                                if qty_rounded > 0:
                                    values = orderpoint._prepare_procurement_values(
                                        qty_rounded,
                                        **group['procurement_values'])
                                    try:
                                        with self._cr.savepoint():
                                            self.env['procurement.group'].run(
                                                orderpoint.product_id,
                                                qty_rounded,
                                                orderpoint.product_uom,
                                                orderpoint.location_id,
                                                orderpoint.name,
                                                orderpoint.name, values)
                                    except UserError as error:
                                        self.env[
                                            'procurement.rule']._log_next_activity(
                                                orderpoint.product_id,
                                                error.name)
                                    self._procurement_from_orderpoint_post_process(
                                        [orderpoint.id])
                                if use_new_cursor:
                                    cr.commit()

                        except OperationalError:
                            if use_new_cursor:
                                orderpoints_noprefetch += [orderpoint.id]
                                cr.rollback()
                                continue
                            else:
                                raise

            try:
                if use_new_cursor:
                    cr.commit()
            except OperationalError:
                if use_new_cursor:
                    cr.rollback()
                    continue
                else:
                    raise

            if use_new_cursor:
                cr.commit()
                cr.close()

        return {}
Example #18
0
    def test_00_delivery_cost(self):
        # In order to test Carrier Cost
        # Create sales order with Normal Delivery Charges

        self.sale_normal_delivery_charges = self.SaleOrder.create({
            'partner_id':
            self.partner_18.id,
            'partner_invoice_id':
            self.partner_18.id,
            'partner_shipping_id':
            self.partner_18.id,
            'pricelist_id':
            self.pricelist.id,
            'order_line': [(0, 0, {
                'name': 'PC Assamble + 2GB RAM',
                'product_id': self.product_4.id,
                'product_uom_qty': 1,
                'product_uom': self.product_uom_unit.id,
                'price_unit': 750.00,
            })],
            'carrier_id':
            self.normal_delivery.id
        })
        # I add delivery cost in Sales order

        self.a_sale = self.AccountAccount.create({
            'code':
            'X2020',
            'name':
            'Product Sales - (test)',
            'user_type_id':
            self.account_data.id,
            'tag_ids': [(6, 0, {self.account_tag_operating.id})]
        })

        self.product_consultant = self.Product.create({
            'sale_ok':
            True,
            'list_price':
            75.0,
            'standard_price':
            30.0,
            'uom_id':
            self.product_uom_hour.id,
            'uom_po_id':
            self.product_uom_hour.id,
            'name':
            'Service',
            'categ_id':
            self.product_category.id,
            'type':
            'service'
        })

        # I add delivery cost in Sales order
        self.sale_normal_delivery_charges.get_delivery_price()
        self.sale_normal_delivery_charges.set_delivery_line()

        # I check sales order after added delivery cost

        line = self.SaleOrderLine.search([
            ('order_id', '=', self.sale_normal_delivery_charges.id),
            ('product_id', '=',
             self.sale_normal_delivery_charges.carrier_id.product_id.id)
        ])
        self.assertEqual(len(line), 1, "Delivery cost is not Added")

        self.assertEqual(
            float_compare(line.price_subtotal, 10.0, precision_digits=2), 0,
            "Delivery cost is not correspond.")

        # I confirm the sales order

        self.sale_normal_delivery_charges.action_confirm()

        # Create one more sales order with Free Delivery Charges

        self.delivery_sale_order_cost = self.SaleOrder.create({
            'partner_id':
            self.partner_4.id,
            'partner_invoice_id':
            self.partner_address_13.id,
            'partner_shipping_id':
            self.partner_address_13.id,
            'pricelist_id':
            self.pricelist.id,
            'order_line': [(0, 0, {
                'name': 'Service on demand',
                'product_id': self.product_consultant.id,
                'product_uom_qty': 24,
                'product_uom': self.product_uom_hour.id,
                'price_unit': 75.00,
            }),
                           (0, 0, {
                               'name': 'On Site Assistance',
                               'product_id': self.product_2.id,
                               'product_uom_qty': 30,
                               'product_uom': self.product_uom_hour.id,
                               'price_unit': 38.25,
                           })],
            'carrier_id':
            self.free_delivery.id
        })

        # I add free delivery cost in Sales order
        self.delivery_sale_order_cost.get_delivery_price()
        self.delivery_sale_order_cost.set_delivery_line()

        # I check sales order after adding delivery cost
        line = self.SaleOrderLine.search([
            ('order_id', '=', self.delivery_sale_order_cost.id),
            ('product_id', '=',
             self.delivery_sale_order_cost.carrier_id.product_id.id)
        ])

        self.assertEqual(len(line), 1, "Delivery cost is not Added")
        self.assertEqual(
            float_compare(line.price_subtotal, 0, precision_digits=2), 0,
            "Delivery cost is not correspond.")

        # I set default delivery policy

        self.default_delivery_policy = self.SaleConfigSetting.create({})

        self.default_delivery_policy.execute()
Example #19
0
    def _action_launch_procurement_rule(self):
        """
        Launch procurement group run method with required/custom fields genrated by a
        sale order line. procurement group will launch '_run_move', '_run_buy' or '_run_manufacture'
        depending on the sale order line product rule.
        """
        precision = self.env['decimal.precision'].precision_get(
            'Product Unit of Measure')
        errors = []
        for line in self:
            if line.state != 'sale' or not line.product_id.type in ('consu',
                                                                    'product'):
                continue
            qty = line._get_qty_procurement()
            if float_compare(qty,
                             line.product_uom_qty,
                             precision_digits=precision) >= 0:
                continue

            group_id = line.order_id.procurement_group_id
            if not group_id:
                group_id = self.env['procurement.group'].create({
                    'name':
                    line.order_id.name,
                    'move_type':
                    line.order_id.picking_policy,
                    'sale_id':
                    line.order_id.id,
                    'partner_id':
                    line.order_id.partner_shipping_id.id,
                })
                line.order_id.procurement_group_id = group_id
            else:
                # In case the procurement group is already created and the order was
                # cancelled, we need to update certain values of the group.
                updated_vals = {}
                if group_id.partner_id != line.order_id.partner_shipping_id:
                    updated_vals.update(
                        {'partner_id': line.order_id.partner_shipping_id.id})
                if group_id.move_type != line.order_id.picking_policy:
                    updated_vals.update(
                        {'move_type': line.order_id.picking_policy})
                if updated_vals:
                    group_id.write(updated_vals)

            values = line._prepare_procurement_values(group_id=group_id)
            product_qty = line.product_uom_qty - qty

            procurement_uom = line.product_uom
            quant_uom = line.product_id.uom_id
            get_param = self.env['ir.config_parameter'].sudo().get_param
            if procurement_uom.id != quant_uom.id and get_param(
                    'stock.propagate_uom') != '1':
                product_qty = line.product_uom._compute_quantity(
                    product_qty, quant_uom, rounding_method='HALF-UP')
                procurement_uom = quant_uom

            try:
                self.env['procurement.group'].run(
                    line.product_id, product_qty, procurement_uom,
                    line.order_id.partner_shipping_id.property_stock_customer,
                    line.name, line.order_id.name, values)
            except UserError as error:
                errors.append(error.name)
        if errors:
            raise UserError('\n'.join(errors))
        return True
Example #20
0
    def test_order_to_invoice(self):

        # I create a new PoS order with 2 units of PC1 at 450 EUR (Tax Incl) and 3 units of PCSC349 at 300 EUR. (Tax Excl)
        self.pos_order_pos1 = self.PosOrder.create({
            'company_id':
            self.company_id,
            'partner_id':
            self.partner1.id,
            'pricelist_id':
            self.partner1.property_product_pricelist.id,
            'lines': [(0, 0, {
                'name': "OL/0001",
                'product_id': self.product3.id,
                'price_unit': 450,
                'discount': 5.0,
                'qty': 2.0,
                'tax_ids': [(6, 0, self.product3.taxes_id.ids)],
            }),
                      (0, 0, {
                          'name': "OL/0002",
                          'product_id': self.product4.id,
                          'price_unit': 300,
                          'discount': 5.0,
                          'qty': 3.0,
                          'tax_ids': [(6, 0, self.product4.taxes_id.ids)],
                      })]
        })

        # I click on the "Make Payment" wizard to pay the PoS order
        context_make_payment = {
            "active_ids": [self.pos_order_pos1.id],
            "active_id": self.pos_order_pos1.id
        }
        self.pos_make_payment = self.PosMakePayment.with_context(
            context_make_payment).create({
                'amount': (450 * 2 + 300 * 3 * 1.05) * 0.95,
            })
        # I click on the validate button to register the payment.
        context_payment = {'active_id': self.pos_order_pos1.id}
        self.pos_make_payment.with_context(context_payment).check()

        # I check that the order is marked as paid and there is no invoice
        # attached to it
        self.assertEqual(self.pos_order_pos1.state, 'paid',
                         "Order should be in paid state.")
        self.assertFalse(self.pos_order_pos1.invoice_id,
                         'Invoice should not be attached to order.')

        # I generate an invoice from the order
        res = self.pos_order_pos1.action_pos_order_invoice()
        self.assertIn('res_id', res, "No invoice created")

        # I test that the total of the attached invoice is correct
        invoice = self.env['account.invoice'].browse(res['res_id'])
        self.assertEqual(
            float_compare(invoice.amount_total, 1752.75, precision_digits=2),
            0, "Invoice not correct")
        """In order to test the reports on Bank Statement defined in point_of_sale module, I create a bank statement line, confirm it and print the reports"""

        # I select the period and journal for the bank statement

        context_journal = {'journal_type': 'bank'}
        self.assertTrue(
            self.AccountBankStatement.with_context(
                context_journal)._default_journal(),
            'Journal has not been selected')
        journal = self.env['account.journal'].create({
            'name':
            'Bank Test',
            'code':
            'BNKT',
            'type':
            'bank',
            'company_id':
            self.company_id,
        })
        # I create a bank statement with Opening and Closing balance 0.
        account_statement = self.AccountBankStatement.create({
            'balance_start':
            0.0,
            'balance_end_real':
            0.0,
            'date':
            time.strftime('%Y-%m-%d'),
            'journal_id':
            journal.id,
            'company_id':
            self.company_id,
            'name':
            'pos session test',
        })
        # I create bank statement line
        account_statement_line = self.AccountBankStatementLine.create({
            'amount':
            1000,
            'partner_id':
            self.partner4.id,
            'statement_id':
            account_statement.id,
            'name':
            'EXT001'
        })
        # I modify the bank statement and set the Closing Balance.
        account_statement.write({
            'balance_end_real': 1000.0,
        })

        # I reconcile the bank statement.
        new_aml_dicts = [{
            'account_id':
            self.partner4.property_account_receivable_id.id,
            'name':
            "EXT001",
            'credit':
            1000.0,
            'debit':
            0.0,
        }]

        account_statement_line.process_reconciliations([{
            'new_aml_dicts':
            new_aml_dicts
        }])

        # I confirm the bank statement using Confirm button

        self.AccountBankStatement.button_confirm_bank()
Example #21
0
    def create_move(self, post_move=True):
        created_moves = self.env['account.move']
        prec = self.env['decimal.precision'].precision_get('Account')
        for line in self:
            if line.move_id:
                raise UserError(
                    _('This depreciation is already linked to a journal entry! Please post or delete it.'
                      ))
            category_id = line.asset_id.category_id
            depreciation_date = self.env.context.get(
                'depreciation_date'
            ) or line.depreciation_date or fields.Date.context_today(self)
            company_currency = line.asset_id.company_id.currency_id
            current_currency = line.asset_id.currency_id
            amount = current_currency.with_context(
                date=depreciation_date).compute(line.amount, company_currency)
            asset_name = line.asset_id.name + ' (%s/%s)' % (
                line.sequence, len(line.asset_id.depreciation_line_ids))
            move_line_1 = {
                'name':
                asset_name,
                'account_id':
                category_id.account_depreciation_id.id,
                'debit':
                0.0 if float_compare(amount, 0.0, precision_digits=prec) > 0
                else -amount,
                'credit':
                amount if float_compare(amount, 0.0, precision_digits=prec) > 0
                else 0.0,
                'journal_id':
                category_id.journal_id.id,
                'partner_id':
                line.asset_id.partner_id.id,
                'analytic_account_id':
                category_id.account_analytic_id.id
                if category_id.type == 'sale' else False,
                'currency_id':
                company_currency != current_currency and current_currency.id
                or False,
                'amount_currency':
                company_currency != current_currency and -1.0 * line.amount
                or 0.0,
            }
            move_line_2 = {
                'name':
                asset_name,
                'account_id':
                category_id.account_depreciation_expense_id.id,
                'credit':
                0.0 if float_compare(amount, 0.0, precision_digits=prec) > 0
                else -amount,
                'debit':
                amount if float_compare(amount, 0.0, precision_digits=prec) > 0
                else 0.0,
                'journal_id':
                category_id.journal_id.id,
                'partner_id':
                line.asset_id.partner_id.id,
                'analytic_account_id':
                category_id.account_analytic_id.id
                if category_id.type == 'purchase' else False,
                'currency_id':
                company_currency != current_currency and current_currency.id
                or False,
                'amount_currency':
                company_currency != current_currency and line.amount or 0.0,
            }
            move_vals = {
                'ref': line.asset_id.code,
                'date': depreciation_date or False,
                'journal_id': category_id.journal_id.id,
                'line_ids': [(0, 0, move_line_1), (0, 0, move_line_2)],
            }
            move = self.env['account.move'].create(move_vals)
            line.write({'move_id': move.id, 'move_check': True})
            created_moves |= move

        if post_move and created_moves:
            created_moves.filtered(lambda m: any(
                m.asset_depreciation_ids.mapped(
                    'asset_id.category_id.open_asset'))).post()
        return [x.id for x in created_moves]
    def action_payslip_done(self):
        res = super(HrPayslip, self).action_payslip_done()
        precision = self.env['decimal.precision'].precision_get('Payroll')

        for slip in self:
            line_ids = []
            debit_sum = 0.0
            credit_sum = 0.0
            date = slip.date or slip.date_to

            name = _('Payslip of %s') % (slip.employee_id.name)
            move_dict = {
                'narration': name,
                'ref': slip.number,
                'journal_id': slip.journal_id.id,
                'date': date,
            }
            for line in slip.details_by_salary_rule_category:
                amount = slip.credit_note and -line.total or line.total
                if float_is_zero(amount, precision_digits=precision):
                    continue
                debit_account_id = line.salary_rule_id.account_debit.id
                credit_account_id = line.salary_rule_id.account_credit.id

                if debit_account_id:
                    debit_line = (0, 0, {
                        'name':
                        line.name,
                        'partner_id':
                        line._get_partner_id(credit_account=False),
                        'account_id':
                        debit_account_id,
                        'journal_id':
                        slip.journal_id.id,
                        'date':
                        date,
                        'debit':
                        amount > 0.0 and amount or 0.0,
                        'credit':
                        amount < 0.0 and -amount or 0.0,
                        'analytic_account_id':
                        line.salary_rule_id.analytic_account_id.id,
                        'tax_line_id':
                        line.salary_rule_id.account_tax_id.id,
                    })
                    line_ids.append(debit_line)
                    debit_sum += debit_line[2]['debit'] - debit_line[2][
                        'credit']

                if credit_account_id:
                    credit_line = (0, 0, {
                        'name':
                        line.name,
                        'partner_id':
                        line._get_partner_id(credit_account=True),
                        'account_id':
                        credit_account_id,
                        'journal_id':
                        slip.journal_id.id,
                        'date':
                        date,
                        'debit':
                        amount < 0.0 and -amount or 0.0,
                        'credit':
                        amount > 0.0 and amount or 0.0,
                        'analytic_account_id':
                        line.salary_rule_id.analytic_account_id.id,
                        'tax_line_id':
                        line.salary_rule_id.account_tax_id.id,
                    })
                    line_ids.append(credit_line)
                    credit_sum += credit_line[2]['credit'] - credit_line[2][
                        'debit']

            if float_compare(credit_sum, debit_sum,
                             precision_digits=precision) == -1:
                acc_id = slip.journal_id.default_credit_account_id.id
                if not acc_id:
                    raise UserError(
                        _('The Expense Journal "%s" has not properly configured the Credit Account!'
                          ) % (slip.journal_id.name))
                adjust_credit = (0, 0, {
                    'name': _('Adjustment Entry'),
                    'partner_id': False,
                    'account_id': acc_id,
                    'journal_id': slip.journal_id.id,
                    'date': date,
                    'debit': 0.0,
                    'credit': debit_sum - credit_sum,
                })
                line_ids.append(adjust_credit)

            elif float_compare(debit_sum,
                               credit_sum,
                               precision_digits=precision) == -1:
                acc_id = slip.journal_id.default_debit_account_id.id
                if not acc_id:
                    raise UserError(
                        _('The Expense Journal "%s" has not properly configured the Debit Account!'
                          ) % (slip.journal_id.name))
                adjust_debit = (0, 0, {
                    'name': _('Adjustment Entry'),
                    'partner_id': False,
                    'account_id': acc_id,
                    'journal_id': slip.journal_id.id,
                    'date': date,
                    'debit': credit_sum - debit_sum,
                    'credit': 0.0,
                })
                line_ids.append(adjust_debit)
            move_dict['line_ids'] = line_ids
            move = self.env['account.move'].create(move_dict)
            slip.write({'move_id': move.id, 'date': date})
            move.post()
        return res
Example #23
0
    def _generate_consumed_move_line(self, qty_to_add, final_lot, lot=False):
        if lot:
            move_lines = self.move_line_ids.filtered(
                lambda ml: ml.lot_id == lot and not ml.lot_produced_id)
        else:
            move_lines = self.move_line_ids.filtered(
                lambda ml: not ml.lot_id and not ml.lot_produced_id)

        # Sanity check: if the product is a serial number and `lot` is already present in the other
        # consumed move lines, raise.
        if lot and self.product_id.tracking == 'serial' and lot in self.move_line_ids.filtered(
                lambda ml: ml.qty_done).mapped('lot_id'):
            raise UserError(
                _('You cannot consume the same serial number twice. Please correct the serial numbers encoded.'
                  ))

        for ml in move_lines:
            rounding = ml.product_uom_id.rounding
            if float_compare(qty_to_add, 0, precision_rounding=rounding) <= 0:
                break
            quantity_to_process = min(qty_to_add,
                                      ml.product_uom_qty - ml.qty_done)
            qty_to_add -= quantity_to_process

            new_quantity_done = (ml.qty_done + quantity_to_process)
            if float_compare(new_quantity_done,
                             ml.product_uom_qty,
                             precision_rounding=rounding) >= 0:
                ml.write({
                    'qty_done': new_quantity_done,
                    'lot_produced_id': final_lot.id
                })
            else:
                new_qty_reserved = ml.product_uom_qty - new_quantity_done
                default = {
                    'product_uom_qty': new_quantity_done,
                    'qty_done': new_quantity_done,
                    'lot_produced_id': final_lot.id
                }
                ml.copy(default=default)
                ml.with_context(bypass_reservation_update=True).write({
                    'product_uom_qty':
                    new_qty_reserved,
                    'qty_done':
                    0
                })

        if float_compare(qty_to_add,
                         0,
                         precision_rounding=self.product_uom.rounding) > 0:
            # Search for a sub-location where the product is available. This might not be perfectly
            # correct if the quantity available is spread in several sub-locations, but at least
            # we should be closer to the reality. Anyway, no reservation is made, so it is still
            # possible to change it afterwards.
            quants = self.env['stock.quant']._gather(self.product_id,
                                                     self.location_id,
                                                     lot_id=lot,
                                                     strict=False)
            available_quantity = self.product_id.uom_id._compute_quantity(
                self.env['stock.quant']._get_available_quantity(
                    self.product_id,
                    self.location_id,
                    lot_id=lot,
                    strict=False), self.product_uom)
            location_id = False
            if float_compare(qty_to_add,
                             available_quantity,
                             precision_rounding=self.product_uom.rounding) < 0:
                location_id = quants.filtered(
                    lambda r: r.quantity > 0)[-1:].location_id

            vals = {
                'move_id':
                self.id,
                'product_id':
                self.product_id.id,
                'location_id':
                location_id.id if location_id else self.location_id.id,
                'location_dest_id':
                self.location_dest_id.id,
                'product_uom_qty':
                0,
                'product_uom_id':
                self.product_uom.id,
                'qty_done':
                qty_to_add,
                'lot_produced_id':
                final_lot.id,
            }
            if lot:
                vals.update({'lot_id': lot.id})
            self.env['stock.move.line'].create(vals)
Example #24
0
    def action_repair_done(self):
        """ Creates stock move for operation and stock move for final product of repair order.
        @return: Move ids of final products

        """
        if self.filtered(lambda repair: not repair.repaired):
            raise UserError(_("Repair must be repaired in order to make the product moves."))
        res = {}
        precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
        Move = self.env['stock.move']
        for repair in self:
            # Try to create move with the appropriate owner
            owner_id = False
            available_qty_owner = self.env['stock.quant']._get_available_quantity(repair.product_id, repair.location_id, repair.lot_id, owner_id=repair.partner_id, strict=True)
            if float_compare(available_qty_owner, repair.product_qty, precision_digits=precision) >= 0:
                owner_id = repair.partner_id.id

            moves = self.env['stock.move']
            for operation in repair.operations:
                move = Move.create({
                    'name': repair.name,
                    'product_id': operation.product_id.id,
                    'product_uom_qty': operation.product_uom_qty,
                    'product_uom': operation.product_uom.id,
                    'partner_id': repair.address_id.id,
                    'location_id': operation.location_id.id,
                    'location_dest_id': operation.location_dest_id.id,
                    'move_line_ids': [(0, 0, {'product_id': operation.product_id.id,
                                           'lot_id': operation.lot_id.id, 
                                           'product_uom_qty': 0,  # bypass reservation here
                                           'product_uom_id': operation.product_uom.id,
                                           'qty_done': operation.product_uom_qty,
                                           'package_id': False,
                                           'result_package_id': False,
                                           'owner_id': owner_id,
                                           'location_id': operation.location_id.id, #TODO: owner stuff
                                           'location_dest_id': operation.location_dest_id.id,})],
                    'repair_id': repair.id,
                    'origin': repair.name,
                })
                moves |= move
                operation.write({'move_id': move.id, 'state': 'done'})
            move = Move.create({
                'name': repair.name,
                'product_id': repair.product_id.id,
                'product_uom': repair.product_uom.id or repair.product_id.uom_id.id,
                'product_uom_qty': repair.product_qty,
                'partner_id': repair.address_id.id,
                'location_id': repair.location_id.id,
                'location_dest_id': repair.location_dest_id.id,
                'move_line_ids': [(0, 0, {'product_id': repair.product_id.id,
                                           'lot_id': repair.lot_id.id, 
                                           'product_uom_qty': 0,  # bypass reservation here
                                           'product_uom_id': repair.product_uom.id or repair.product_id.uom_id.id,
                                           'qty_done': repair.product_qty,
                                           'package_id': False,
                                           'result_package_id': False,
                                           'owner_id': owner_id,
                                           'location_id': repair.location_id.id, #TODO: owner stuff
                                           'location_dest_id': repair.location_dest_id.id,})],
                'repair_id': repair.id,
                'origin': repair.name,
            })
            consumed_lines = moves.mapped('move_line_ids')
            produced_lines = move.move_line_ids
            moves |= move
            moves._action_done()
            produced_lines.write({'consume_line_ids': [(6, 0, consumed_lines.ids)]})
            res[repair.id] = move.id
        return res