Exemplo n.º 1
0
    def _compute_quantities_dict(self, lot_id, owner_id, package_id, from_date=False, to_date=False):
        domain_quant_loc, domain_move_in_loc, domain_move_out_loc = self._get_domain_locations()
        domain_quant = [('product_id', 'in', self.ids)] + domain_quant_loc
        dates_in_the_past = False
        # only to_date as to_date will correspond to qty_available
        to_date = fields.Datetime.to_datetime(to_date)
        if to_date and to_date < fields.Datetime.now():
            dates_in_the_past = True

        domain_move_in = [('product_id', 'in', self.ids)] + domain_move_in_loc
        domain_move_out = [('product_id', 'in', self.ids)] + domain_move_out_loc
        if lot_id is not None:
            domain_quant += [('lot_id', '=', lot_id)]
        if owner_id is not None:
            domain_quant += [('owner_id', '=', owner_id)]
            domain_move_in += [('restrict_partner_id', '=', owner_id)]
            domain_move_out += [('restrict_partner_id', '=', owner_id)]
        if package_id is not None:
            domain_quant += [('package_id', '=', package_id)]
        if dates_in_the_past:
            domain_move_in_done = list(domain_move_in)
            domain_move_out_done = list(domain_move_out)
        if from_date:
            domain_move_in += [('date', '>=', from_date)]
            domain_move_out += [('date', '>=', from_date)]
        if to_date:
            domain_move_in += [('date', '<=', to_date)]
            domain_move_out += [('date', '<=', to_date)]

        Move = self.env['stock.move']
        Quant = self.env['stock.quant']
        domain_move_in_todo = [('state', 'in', ('waiting', 'confirmed', 'assigned', 'partially_available'))] + domain_move_in
        domain_move_out_todo = [('state', 'in', ('waiting', 'confirmed', 'assigned', 'partially_available'))] + domain_move_out
        moves_in_res = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_in_todo, ['product_id', 'product_qty'], ['product_id'], orderby='id'))
        moves_out_res = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_out_todo, ['product_id', 'product_qty'], ['product_id'], orderby='id'))
        quants_res = dict((item['product_id'][0], item['quantity']) for item in Quant.read_group(domain_quant, ['product_id', 'quantity'], ['product_id'], orderby='id'))
        if dates_in_the_past:
            # Calculate the moves that were done before now to calculate back in time (as most questions will be recent ones)
            domain_move_in_done = [('state', '=', 'done'), ('date', '>', to_date)] + domain_move_in_done
            domain_move_out_done = [('state', '=', 'done'), ('date', '>', to_date)] + domain_move_out_done
            moves_in_res_past = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_in_done, ['product_id', 'product_qty'], ['product_id'], orderby='id'))
            moves_out_res_past = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_out_done, ['product_id', 'product_qty'], ['product_id'], orderby='id'))

        res = dict()
        for product in self.with_context(prefetch_fields=False):
            product_id = product.id
            rounding = product.uom_id.rounding
            res[product_id] = {}
            if dates_in_the_past:
                qty_available = quants_res.get(product_id, 0.0) - moves_in_res_past.get(product_id, 0.0) + moves_out_res_past.get(product_id, 0.0)
            else:
                qty_available = quants_res.get(product_id, 0.0)
            res[product_id]['qty_available'] = float_round(qty_available, precision_rounding=rounding)
            res[product_id]['incoming_qty'] = float_round(moves_in_res.get(product_id, 0.0), precision_rounding=rounding)
            res[product_id]['outgoing_qty'] = float_round(moves_out_res.get(product_id, 0.0), precision_rounding=rounding)
            res[product_id]['virtual_available'] = float_round(
                qty_available + res[product_id]['incoming_qty'] - res[product_id]['outgoing_qty'],
                precision_rounding=rounding)

        return res
Exemplo n.º 2
0
 def _compute_duration_display(self):
     for allocation in self:
         allocation.duration_display = '%g %s' % (
             (float_round(allocation.number_of_hours_display, precision_digits=2)
             if allocation.type_request_unit == 'hour'
             else float_round(allocation.number_of_days_display, precision_digits=2)),
             _('hours') if allocation.type_request_unit == 'hour' else _('days'))
Exemplo n.º 3
0
 def trans_rec_get(self):
     context = self._context or {}
     credit = debit = 0
     lines = self.env['account.move.line'].browse(context.get('active_ids', []))
     for line in lines:
         if not line.reconciled:
             credit += line.credit
             debit += line.debit
     precision = self.env.user.company_id.currency_id.decimal_places
     writeoff = float_round(debit - credit, precision_digits=precision)
     credit = float_round(credit, precision_digits=precision)
     debit = float_round(debit, precision_digits=precision)
     return {'trans_nbr': len(lines), 'credit': credit, 'debit': debit, 'writeoff': writeoff}
Exemplo n.º 4
0
 def _quant_split(self, qty):
     self.ensure_one()
     rounding = self.product_id.uom_id.rounding
     if float_compare(abs(self.qty), abs(qty), precision_rounding=rounding) <= 0: # if quant <= qty in abs, take it entirely
         return False
     qty_round = float_round(qty, precision_rounding=rounding)
     new_qty_round = float_round(self.qty - qty, precision_rounding=rounding)
     # Fetch the history_ids manually as it will not do a join with the stock moves then (=> a lot faster)
     self._cr.execute("""SELECT move_id FROM stock_quant_move_rel WHERE quant_id = %s""", (self.id,))
     res = self._cr.fetchall()
     new_quant = self.sudo().copy(default={'qty': new_qty_round, 'history_ids': [(4, x[0]) for x in res]})
     self.sudo().write({'qty': qty_round})
     return new_quant
Exemplo n.º 5
0
    def _quant_create_from_move(
        self,
        qty,
        move,
        lot_id=False,
        owner_id=False,
        src_package_id=False,
        dest_package_id=False,
        force_location_from=False,
        force_location_to=False,
    ):
        """Create a quant in the destination location and create a negative
        quant in the source location if it's an internal location. """
        price_unit = move.get_price_unit()
        location = force_location_to or move.location_dest_id
        rounding = move.product_id.uom_id.rounding
        vals = {
            "product_id": move.product_id.id,
            "location_id": location.id,
            "qty": float_round(qty, precision_rounding=rounding),
            "cost": price_unit,
            "history_ids": [(4, move.id)],
            "in_date": datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT),
            "company_id": move.company_id.id,
            "lot_id": lot_id,
            "owner_id": owner_id,
            "package_id": dest_package_id,
        }
        if move.location_id.usage == "internal":
            # if we were trying to move something from an internal location and reach here (quant creation),
            # it means that a negative quant has to be created as well.
            negative_vals = vals.copy()
            negative_vals["location_id"] = force_location_from and force_location_from.id or move.location_id.id
            negative_vals["qty"] = float_round(-qty, precision_rounding=rounding)
            negative_vals["cost"] = price_unit
            negative_vals["negative_move_id"] = move.id
            negative_vals["package_id"] = src_package_id
            negative_quant_id = self.sudo().create(negative_vals)
            vals.update({"propagated_from_id": negative_quant_id.id})

        picking_type = move.picking_id and move.picking_id.picking_type_id or False
        if (
            lot_id
            and move.product_id.tracking == "serial"
            and (not picking_type or (picking_type.use_create_lots or picking_type.use_existing_lots))
        ):
            if qty != 1.0:
                raise UserError(_("You should only receive by the piece with the same serial number"))

        # create the quant as superuser, because we want to restrict the creation of quant manually: we should always use this method to create quants
        return self.sudo().create(vals)
Exemplo n.º 6
0
    def get_opening_move_differences(self, opening_move_lines):
        currency = self.currency_id
        balancing_move_line = opening_move_lines.filtered(lambda x: x.account_id == self.get_unaffected_earnings_account())

        debits_sum = credits_sum = 0.0
        for line in opening_move_lines:
            if line != balancing_move_line:
                #skip the autobalancing move line
                debits_sum += line.debit
                credits_sum += line.credit

        difference = abs(debits_sum - credits_sum)
        debit_diff = (debits_sum > credits_sum) and float_round(difference, precision_rounding=currency.rounding) or 0.0
        credit_diff = (debits_sum < credits_sum) and float_round(difference, precision_rounding=currency.rounding) or 0.0
        return debit_diff, credit_diff
Exemplo n.º 7
0
    def _put_in_pack(self):
        package = False
        for pick in self:
            operations = pick.move_line_ids.filtered(lambda o: o.qty_done > 0 and not o.result_package_id)
            operation_ids = self.env['stock.move.line']
            if operations:
                package = self.env['stock.quant.package'].create({})
                for operation in operations:
                    if float_compare(operation.qty_done, operation.product_uom_qty, precision_rounding=operation.product_uom_id.rounding) >= 0:
                        operation_ids |= operation
                    else:
                        quantity_left_todo = float_round(
                            operation.product_uom_qty - operation.qty_done,
                            precision_rounding=operation.product_uom_id.rounding,
                            rounding_method='UP')
                        done_to_keep = operation.qty_done
                        new_operation = operation.copy(
                            default={'product_uom_qty': 0, 'qty_done': operation.qty_done})
                        operation.write({'product_uom_qty': quantity_left_todo, 'qty_done': 0.0})
                        new_operation.write({'product_uom_qty': done_to_keep})
                        operation_ids |= new_operation

                operation_ids.write({'result_package_id': package.id})
            else:
                raise UserError(_('Please process some quantities to put in the pack first!'))
        return package
Exemplo n.º 8
0
Arquivo: hr.py Projeto: bitctrl/odoo
 def _inverse_remaining_leaves(self):
     status_list = self.env['hr.leave.type'].search([('limit', '=', False)])
     # Create leaves (adding remaining leaves) or raise (reducing remaining leaves)
     actual_remaining = self._get_remaining_leaves()
     for employee in self.filtered(lambda employee: employee.remaining_leaves):
         # check the status list. This is done here and not before the loop to avoid raising
         # exception on employee creation (since we are in a computed field).
         if len(status_list) != 1:
             raise UserError(_("The feature behind the field 'Remaining Legal Leaves' can only be used when there is only one "
                 "leave type with the option 'Allow to Override Limit' unchecked. (%s Found). "
                 "Otherwise, the update is ambiguous as we cannot decide on which leave type the update has to be done. "
                 "\n You may prefer to use the classic menus 'Leave Requests' and 'Allocation Requests' located in Leaves Application "
                 "to manage the leave days of the employees if the configuration does not allow to use this field.") % (len(status_list)))
         status = status_list[0] if status_list else None
         if not status:
             continue
         # if a status is found, then compute remaing leave for current employee
         difference = float_round(employee.remaining_leaves - actual_remaining.get(employee.id, 0), precision_digits=2)
         if difference > 0:
             leave = self.env['hr.leave.allocation'].create({
                 'name': _('Allocation for %s') % employee.name,
                 'employee_id': employee.id,
                 'holiday_status_id': status.id,
                 'holiday_type': 'employee',
                 'number_of_days_temp': difference
             })
             leave.action_approve()
             if leave.validation_type == 'both':
                 leave.action_validate()
         elif difference < 0:
             raise UserError(_('You cannot reduce validated allocation requests.'))
Exemplo n.º 9
0
 def _onchange_hours_per_day(self):
     attendances = self._get_global_attendances()
     hour_count = 0.0
     for attendance in attendances:
         hour_count += attendance.hour_to - attendance.hour_from
     if attendances:
         self.hours_per_day = float_round(hour_count / float(len(set(attendances.mapped('dayofweek')))), precision_digits=2)
Exemplo n.º 10
0
 def ogone_form_generate_values(self, values):
     base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
     ogone_tx_values = dict(values)
     param_plus = {
         'return_url': ogone_tx_values.pop('return_url', False)
     }
     temp_ogone_tx_values = {
         'PSPID': self.ogone_pspid,
         'ORDERID': values['reference'],
         'AMOUNT': float_repr(float_round(values['amount'], 2) * 100, 0),
         'CURRENCY': values['currency'] and values['currency'].name or '',
         'LANGUAGE': values.get('partner_lang'),
         'CN': values.get('partner_name'),
         'EMAIL': values.get('partner_email'),
         'OWNERZIP': values.get('partner_zip'),
         'OWNERADDRESS': values.get('partner_address'),
         'OWNERTOWN': values.get('partner_city'),
         'OWNERCTY': values.get('partner_country') and values.get('partner_country').code or '',
         'OWNERTELNO': values.get('partner_phone'),
         'ACCEPTURL': urls.url_join(base_url, OgoneController._accept_url),
         'DECLINEURL': urls.url_join(base_url, OgoneController._decline_url),
         'EXCEPTIONURL': urls.url_join(base_url, OgoneController._exception_url),
         'CANCELURL': urls.url_join(base_url, OgoneController._cancel_url),
         'PARAMPLUS': url_encode(param_plus),
     }
     if self.save_token in ['ask', 'always']:
         temp_ogone_tx_values.update({
             'ALIAS': 'ODOO-NEW-ALIAS-%s' % time.time(),    # something unique,
             'ALIASUSAGE': values.get('alias_usage') or self.ogone_alias_usage,
         })
     shasign = self._ogone_generate_shasign('in', temp_ogone_tx_values)
     temp_ogone_tx_values['SHASIGN'] = shasign
     ogone_tx_values.update(temp_ogone_tx_values)
     return ogone_tx_values
Exemplo n.º 11
0
Arquivo: hr.py Projeto: bud-e/odoo
 def _compute_leaves_count(self):
     all_leaves = self.env['hr.leave.report'].read_group([
         ('employee_id', 'in', self.ids),
         ('state', '=', 'validate')
     ], fields=['number_of_days', 'employee_id'], groupby=['employee_id'])
     mapping = dict([(leave['employee_id'][0], leave['number_of_days']) for leave in all_leaves])
     for employee in self:
         employee.leaves_count = float_round(mapping.get(employee.id, 0), precision_digits=2)
Exemplo n.º 12
0
 def name_get(self):
     if not self._context.get('employee_id'):
         # leave counts is based on employee_id, would be inaccurate if not based on correct employee
         return super(HolidaysType, self).name_get()
     res = []
     for record in self:
         name = record.name
         if record.allocation_type != 'no':
             name = "%(name)s (%(count)s)" % {
                 'name': name,
                 'count': _('%g remaining out of %g') % (
                     float_round(record.virtual_remaining_leaves, precision_digits=2) or 0.0,
                     float_round(record.max_leaves, precision_digits=2) or 0.0,
                 )
             }
         res.append((record.id, name))
     return res
Exemplo n.º 13
0
 def _compute_mrp_product_qty(self):
     date_from = fields.Datetime.to_string(fields.datetime.now() - timedelta(days=365))
     #TODO: state = done?
     domain = [('state', '=', 'done'), ('product_id', 'in', self.ids), ('date_planned_start', '>', date_from)]
     read_group_res = self.env['mrp.production'].read_group(domain, ['product_id', 'product_uom_qty'], ['product_id'])
     mapped_data = dict([(data['product_id'][0], data['product_uom_qty']) for data in read_group_res])
     for product in self:
         product.mrp_product_qty = float_round(mapped_data.get(product.id, 0), precision_rounding=product.uom_id.rounding)
Exemplo n.º 14
0
    def _free_reservation(self, product_id, location_id, quantity, lot_id=None, package_id=None, owner_id=None, ml_to_ignore=None):
        """ When editing a done move line or validating one with some forced quantities, it is
        possible to impact quants that were not reserved. It is therefore necessary to edit or
        unlink the move lines that reserved a quantity now unavailable.

        :param ml_to_ignore: recordset of `stock.move.line` that should NOT be unreserved
        """
        self.ensure_one()

        if ml_to_ignore is None:
            ml_to_ignore = self.env['stock.move.line']
        ml_to_ignore |= self

        # Check the available quantity, with the `strict` kw set to `True`. If the available
        # quantity is greather than the quantity now unavailable, there is nothing to do.
        available_quantity = self.env['stock.quant']._get_available_quantity(
            product_id, location_id, lot_id=lot_id, package_id=package_id, owner_id=owner_id, strict=True
        )
        if quantity > available_quantity:
            # We now have to find the move lines that reserved our now unavailable quantity. We
            # take care to exclude ourselves and the move lines were work had already been done.
            oudated_move_lines_domain = [
                ('move_id.state', 'not in', ['done', 'cancel']),
                ('product_id', '=', product_id.id),
                ('lot_id', '=', lot_id.id if lot_id else False),
                ('location_id', '=', location_id.id),
                ('owner_id', '=', owner_id.id if owner_id else False),
                ('package_id', '=', package_id.id if package_id else False),
                ('product_qty', '>', 0.0),
                ('id', 'not in', ml_to_ignore.ids),
            ]
            oudated_candidates = self.env['stock.move.line'].search(oudated_move_lines_domain)

            # As the move's state is not computed over the move lines, we'll have to manually
            # recompute the moves which we adapted their lines.
            move_to_recompute_state = self.env['stock.move']

            rounding = self.product_uom_id.rounding
            for candidate in oudated_candidates:
                if float_compare(candidate.product_qty, quantity, precision_rounding=rounding) <= 0:
                    quantity -= candidate.product_qty
                    move_to_recompute_state |= candidate.move_id
                    if candidate.qty_done:
                        candidate.product_uom_qty = 0.0
                    else:
                        candidate.unlink()
                else:
                    # split this move line and assign the new part to our extra move
                    quantity_split = float_round(
                        candidate.product_qty - quantity,
                        precision_rounding=self.product_uom_id.rounding,
                        rounding_method='UP')
                    candidate.product_uom_qty = self.product_id.uom_id._compute_quantity(quantity_split, candidate.product_uom_id, rounding_method='HALF-UP')
                    quantity -= quantity_split
                    move_to_recompute_state |= candidate.move_id
                if quantity == 0.0:
                    break
            move_to_recompute_state._recompute_state()
Exemplo n.º 15
0
    def _quant_create_from_move(self, qty, move, lot_id=False, owner_id=False,
                                src_package_id=False, dest_package_id=False,
                                force_location_from=False, force_location_to=False):
        '''Create a quant in the destination location and create a negative
        quant in the source location if it's an internal location. '''
        price_unit = move.get_price_unit()
        location = force_location_to or move.location_dest_id
        rounding = move.product_id.uom_id.rounding
        vals = {
            'product_id': move.product_id.id,
            'location_id': location.id,
            'qty': float_round(qty, precision_rounding=rounding),
            'cost': price_unit,
            'history_ids': [(4, move.id)],
            'in_date': datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT),
            'company_id': move.company_id.id,
            'lot_id': lot_id,
            'owner_id': owner_id,
            'package_id': dest_package_id,
        }
        if move.location_id.usage == 'internal':
            # if we were trying to move something from an internal location and reach here (quant creation),
            # it means that a negative quant has to be created as well.
            negative_vals = vals.copy()
            negative_vals['location_id'] = force_location_from and force_location_from.id or move.location_id.id
            negative_vals['qty'] = float_round(-qty, precision_rounding=rounding)
            negative_vals['cost'] = price_unit
            negative_vals['negative_move_id'] = move.id
            negative_vals['package_id'] = src_package_id
            negative_quant_id = self.sudo().create(negative_vals)
            vals.update({'propagated_from_id': negative_quant_id.id})

        # In case of serial tracking, check if the product does not exist somewhere internally already
        picking_type = move.picking_id and move.picking_id.picking_type_id or False
        if lot_id and move.product_id.tracking == 'serial' and (not picking_type or (picking_type.use_create_lots or picking_type.use_existing_lots)):
            if qty != 1.0:
                raise UserError(_('You should only receive by the piece with the same serial number'))
            other_quants = self.search([('product_id', '=', move.product_id.id), ('lot_id', '=', lot_id),
                                        ('qty', '>', 0.0), ('location_id.usage', '=', 'internal')])
            if other_quants:
                lot_name = self.env['stock.production.lot'].browse(lot_id).name
                raise UserError(_('The serial number %s is already in stock.') % lot_name + _("Otherwise make sure the right stock/owner is set."))

        # create the quant as superuser, because we want to restrict the creation of quant manually: we should always use this method to create quants
        return self.sudo().create(vals)
Exemplo n.º 16
0
    def _compute_timesheet_revenue(self):
        for invoice in self:
            for invoice_line in invoice.invoice_line_ids.filtered(lambda line: line.product_id.type == 'service'):
                uninvoiced_timesheet_lines = self.env['account.analytic.line'].sudo().search([
                    ('so_line', 'in', invoice_line.sale_line_ids.ids),
                    ('project_id', '!=', False),
                    ('timesheet_invoice_id', '=', False),
                    ('timesheet_invoice_type', 'in', ['billable_time', 'billable_fixed'])
                ]).with_context(create=True)  # context key required to avoid loop

                # NOTE JEM : changing quantity (or unit price) of invoice line does not impact the revenue calculation. (FP specs)
                if uninvoiced_timesheet_lines:
                    precision = invoice_line.currency_id.decimal_places
                    # delivered : update revenue with the prorata of number of hours on the timesheet line
                    if invoice_line.product_id.invoice_policy == 'delivery':
                        invoiced_price_per_hour = float_round(invoice_line.price_subtotal / float(sum(uninvoiced_timesheet_lines.mapped('unit_amount'))), precision)
                        # invoicing analytic lines of different currency
                        total_revenue_per_currency = dict.fromkeys(uninvoiced_timesheet_lines.mapped('company_currency_id').ids, 0.0)
                        for index, timesheet_line in enumerate(uninvoiced_timesheet_lines):
                            if index+1 != len(uninvoiced_timesheet_lines):
                                line_revenue = invoice_line.currency_id.compute(invoiced_price_per_hour, timesheet_line.company_currency_id) * timesheet_line.unit_amount
                                total_revenue_per_currency[timesheet_line.company_currency_id.id] += line_revenue
                            else:  # last line: add the difference to avoid rounding problem
                                total_revenue = sum([self.env['res.currency'].browse(currency_id).compute(amount, timesheet_line.company_currency_id) for currency_id, amount in total_revenue_per_currency.items()])
                                line_revenue = invoice_line.currency_id.compute(invoice_line.price_subtotal, timesheet_line.company_currency_id) - total_revenue
                            timesheet_line.write({
                                'timesheet_invoice_id': invoice.id,
                                'timesheet_revenue': timesheet_line.company_currency_id.round(line_revenue),
                            })

                    # ordered : update revenue with the prorata of theorical revenue
                    elif invoice_line.product_id.invoice_policy == 'order':
                        zero_timesheet_revenue = uninvoiced_timesheet_lines.filtered(lambda line: line.timesheet_revenue == 0.0)
                        no_zero_timesheet_revenue = uninvoiced_timesheet_lines.filtered(lambda line: line.timesheet_revenue != 0.0)

                        # timesheet with zero theorical revenue keep the same revenue, but become invoiced (invoice_id set)
                        zero_timesheet_revenue.write({'timesheet_invoice_id': invoice.id})

                        # invoicing analytic lines of different currency
                        total_revenue_per_currency = dict.fromkeys(no_zero_timesheet_revenue.mapped('company_currency_id').ids, 0.0)

                        for index, timesheet_line in enumerate(no_zero_timesheet_revenue):
                            if index+1 != len(no_zero_timesheet_revenue):
                                price_subtotal_inv = invoice_line.currency_id.compute(invoice_line.price_subtotal, timesheet_line.company_currency_id)
                                price_subtotal_sol = timesheet_line.so_line.currency_id.compute(timesheet_line.so_line.price_subtotal, timesheet_line.company_currency_id)
                                line_revenue = timesheet_line.timesheet_revenue * price_subtotal_inv / price_subtotal_sol
                                total_revenue_per_currency[timesheet_line.company_currency_id.id] += line_revenue
                            else:  # last line: add the difference to avoid rounding problem
                                last_price_subtotal_inv = invoice_line.currency_id.compute(invoice_line.price_subtotal, timesheet_line.company_currency_id)
                                total_revenue = sum([self.env['res.currency'].browse(currency_id).compute(amount, timesheet_line.company_currency_id) for currency_id, amount in total_revenue_per_currency.items()])
                                line_revenue = last_price_subtotal_inv - total_revenue

                            timesheet_line.write({
                                'timesheet_invoice_id': invoice.id,
                                'timesheet_revenue': timesheet_line.company_currency_id.round(line_revenue),
                            })
Exemplo n.º 17
0
 def _compute_purchased_product_qty(self):
     date_from = fields.Datetime.to_string(fields.datetime.now() - timedelta(days=365))
     domain = [
         ('state', 'in', ['purchase', 'done']),
         ('product_id', 'in', self.mapped('id')),
         ('date_order', '>', date_from)
     ]
     PurchaseOrderLines = self.env['purchase.order.line'].search(domain)
     order_lines = self.env['purchase.order.line'].read_group(domain, ['product_id', 'product_uom_qty'], ['product_id'])
     purchased_data = dict([(data['product_id'][0], data['product_uom_qty']) for data in order_lines])
     for product in self:
         product.purchased_product_qty = float_round(purchased_data.get(product.id, 0), precision_rounding=product.uom_id.rounding)
Exemplo n.º 18
0
 def _get_remaining_qty(self):
     if self.package_id and not self.product_id:
         # dont try to compute the remaining quantity for packages because it's not relevant (a package could include different products).
         # should use _get_remaining_prod_quantities instead
         # TDE FIXME: actually resolve the comment hereabove
         self.remaining_qty = 0
     else:
         qty = self.product_qty
         if self.product_uom_id:
             qty = self.product_uom_id._compute_quantity(self.product_qty, self.product_id.uom_id)
         for record in self.linked_move_operation_ids:
             qty -= record.qty
         self.remaining_qty = float_round(qty, precision_rounding=self.product_id.uom_id.rounding)
Exemplo n.º 19
0
 def _prepare_stock_return_picking_line_vals_from_move(self, stock_move):
     quantity = stock_move.product_qty - sum(
         stock_move.move_dest_ids
         .filtered(lambda m: m.state in ['partially_available', 'assigned', 'done'])
         .mapped('move_line_ids.product_qty')
     )
     quantity = float_round(quantity, precision_rounding=stock_move.product_uom.rounding)
     return {
         'product_id': stock_move.product_id.id,
         'quantity': quantity,
         'move_id': stock_move.id,
         'uom_id': stock_move.product_id.uom_id.id,
     }
Exemplo n.º 20
0
    def _compute_quantities_dict(self, lot_id, owner_id, package_id, from_date=False, to_date=False):
        domain_quant_loc, domain_move_in_loc, domain_move_out_loc = self._get_domain_locations()

        domain_quant = [('product_id', 'in', self.ids)] + domain_quant_loc
        domain_move_in = [('state', 'not in', ('done', 'cancel', 'draft')), ('product_id', 'in', self.ids)] + domain_move_in_loc
        domain_move_out = [('state', 'not in', ('done', 'cancel', 'draft')), ('product_id', 'in', self.ids)] + domain_move_out_loc
        if from_date:
            domain_move_in += [('date', '>=', from_date)]
            domain_move_out += [('date', '>=', from_date)]
        if to_date:
            domain_move_in += [('date', '<=', to_date)]
            domain_move_out += [('date', '<=', to_date)]
        if lot_id:
            domain_quant += [('lot_id', '=', lot_id)]
        if owner_id:
            domain_quant += [('owner_id', '=', owner_id)]
            domain_move_in += [('restrict_partner_id', '=', owner_id)]
            domain_move_out += [('restrict_partner_id', '=', owner_id)]
        if package_id:
            domain_quant += [('package_id', '=', package_id)]

        Move = self.env['stock.move']
        Quant = self.env['stock.quant']
        moves_in_res = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_in, ['product_id', 'product_qty'], ['product_id']))
        moves_out_res = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_out, ['product_id', 'product_qty'], ['product_id']))
        quants_res = dict((item['product_id'][0], item['qty']) for item in Quant.read_group(domain_quant, ['product_id', 'qty'], ['product_id']))

        res = dict()
        for product in self.with_context(prefetch_fields=False):
            res[product.id] = {}
            res[product.id]['qty_available'] = float_round(quants_res.get(product.id, 0.0), precision_rounding=product.uom_id.rounding)
            res[product.id]['incoming_qty'] = float_round(moves_in_res.get(product.id, 0.0), precision_rounding=product.uom_id.rounding)
            res[product.id]['outgoing_qty'] = float_round(moves_out_res.get(product.id, 0.0), precision_rounding=product.uom_id.rounding)
            res[product.id]['virtual_available'] = float_round(
                res[product.id]['qty_available'] + res[product.id]['incoming_qty'] - res[product.id]['outgoing_qty'],
                precision_rounding=product.uom_id.rounding)
        return res
Exemplo n.º 21
0
    def _compute_sales_count(self):
        r = {}
        if not self.user_has_groups('sales_team.group_sale_salesman'):
            return r

        date_from = fields.Datetime.to_string(fields.datetime.now() - timedelta(days=365))
        domain = [
            ('state', 'in', ['sale', 'done']),
            ('product_id', 'in', self.ids),
            ('date', '>', date_from)
        ]
        for group in self.env['sale.report'].read_group(domain, ['product_id', 'product_uom_qty'], ['product_id']):
            r[group['product_id'][0]] = group['product_uom_qty']
        for product in self:
            product.sales_count = float_round(r.get(product.id, 0), precision_rounding=product.uom_id.rounding)
        return r
Exemplo n.º 22
0
    def _create_stripe_refund(self):
        api_url_refund = 'https://%s/refunds' % (self.acquirer_id._get_stripe_api_url())

        refund_params = {
            'charge': self.acquirer_reference,
            'amount': int(float_round(self.amount * 100, 2)), # by default, stripe refund the full amount (we don't really need to specify the value)
            'metadata[reference]': self.reference,
        }

        _logger.info('_create_stripe_refund: Sending values to URL %s, values:\n%s', api_url_refund, pprint.pformat(refund_params))
        r = requests.post(api_url_refund,
                            auth=(self.acquirer_id.stripe_secret_key, ''),
                            params=refund_params,
                            headers=STRIPE_HEADERS)
        res = r.json()
        _logger.info('_create_stripe_refund: Values received:\n%s', pprint.pformat(res))
        return res
Exemplo n.º 23
0
    def value_to_html(self, value, options):
        if 'decimal_precision' in options:
            precision = self.env['decimal.precision'].search([('name', '=', options['decimal_precision'])]).digits
        else:
            precision = options['precision']

        if precision is None:
            fmt = '%f'
        else:
            value = float_utils.float_round(value, precision_digits=precision)
            fmt = '%.{precision}f'.format(precision=precision)

        formatted = self.user_lang().format(fmt, value, grouping=True).replace(r'-', u'-\N{ZERO WIDTH NO-BREAK SPACE}')

        # %f does not strip trailing zeroes. %g does but its precision causes
        # it to switch to scientific notation starting at a million *and* to
        # strip decimals. So use %f and if no precision was specified manually
        # strip trailing 0.
        if precision is None:
            formatted = re.sub(r'(?:(0|\d+?)0+)$', r'\1', formatted)

        return pycompat.to_text(formatted)
Exemplo n.º 24
0
    def value_to_html(self, value, options):
        if "decimal_precision" in options:
            precision = self.env["decimal.precision"].search([("name", "=", options["decimal_precision"])]).digits
        else:
            precision = options["precision"]

        if precision is None:
            fmt = "%f"
        else:
            value = float_utils.float_round(value, precision_digits=precision)
            fmt = "%.{precision}f".format(precision=precision)

        formatted = self.user_lang().format(fmt, value, grouping=True)

        # %f does not strip trailing zeroes. %g does but its precision causes
        # it to switch to scientific notation starting at a million *and* to
        # strip decimals. So use %f and if no precision was specified manually
        # strip trailing 0.
        if precision is None:
            formatted = re.sub(r"(?:(0|\d+?)0+)$", r"\1", formatted)

        return unicodifier(formatted)
Exemplo n.º 25
0
    def default_get(self, fields):
        if len(self.env.context.get('active_ids', list())) > 1:
            raise UserError(_("You may only return one picking at a time!"))
        res = super(ReturnPicking, self).default_get(fields)

        move_dest_exists = False
        product_return_moves = []
        picking = self.env['stock.picking'].browse(self.env.context.get('active_id'))
        if picking:
            res.update({'picking_id': picking.id})
            if picking.state != 'done':
                raise UserError(_("You may only return Done pickings"))
            for move in picking.move_lines:
                if move.scrapped:
                    continue
                if move.move_dest_ids:
                    move_dest_exists = True
                quantity = move.product_qty - sum(move.move_dest_ids.filtered(lambda m: m.state in ['partially_available', 'assigned', 'done']).\
                                                  mapped('move_line_ids').mapped('product_qty'))
                quantity = float_round(quantity, precision_rounding=move.product_uom.rounding)
                product_return_moves.append((0, 0, {'product_id': move.product_id.id, 'quantity': quantity, 'move_id': move.id, 'uom_id': move.product_id.uom_id.id}))

            if not product_return_moves:
                raise UserError(_("No products to return (only lines in Done state and not fully returned yet can be returned)!"))
            if 'product_return_moves' in fields:
                res.update({'product_return_moves': product_return_moves})
            if 'move_dest_exists' in fields:
                res.update({'move_dest_exists': move_dest_exists})
            if 'parent_location_id' in fields and picking.location_id.usage == 'internal':
                res.update({'parent_location_id': picking.picking_type_id.warehouse_id and picking.picking_type_id.warehouse_id.view_location_id.id or picking.location_id.location_id.id})
            if 'original_location_id' in fields:
                res.update({'original_location_id': picking.location_id.id})
            if 'location_id' in fields:
                location_id = picking.location_id.id
                if picking.picking_type_id.return_picking_type_id.default_location_dest_id.return_location:
                    location_id = picking.picking_type_id.return_picking_type_id.default_location_dest_id.id
                res['location_id'] = location_id
        return res
Exemplo n.º 26
0
 def do_batch_transfer(self):
     """
     Validate a batch of internal transfers.
     Only allow Internal Transfer type.
     Only allow Available pickings.
     Only allow totally incomplete picking lines.
     Skip transfers requiring serial numbers.
     Skip transfers requiring quality checks.
     """
     for pick in self:
         if not pick.picking_type_id == self.env.ref('stock.picking_type_internal'):
             raise UserError(_('Transfers must be type "Internal Transfers". (%s)' % pick))
         if not pick.state == 'assigned':
             raise UserError(_('Transfer must be in the "Available" state. (%s)' % pick))
         if not all([x.qty_done == 0.0 for x in pick.pack_operation_ids]):
             raise UserError(_('We can only validate batches with NO completed lines. (%s)' % pick))
         if not pick.move_lines and not pick.pack_operation_ids:
             raise UserError(_('Create some Initial Demand or Mark as Todo and create some Operations. (%s)' % pick))
         picking_type = pick.picking_type_id
         serials_required = False
         if picking_type.use_create_lots or picking_type.use_existing_lots:
             for pack in pick.pack_operation_ids:
                 if pack.product_id and pack.product_id.tracking != 'none':
                     serials_required = True
                     # raise UserError(_('Lots/serials required, specify those first! (%s)' % pick))
         if pick.check_todo or serials_required:
             continue
         for pack in pick.pack_operation_ids:
             if pack.product_qty > 0:
                 this_qty = float_round(
                     pack.product_qty,
                     precision_rounding=self.product_id.uom_id.rounding)
                 pack.write({'qty_done': this_qty})
             else:
                 pack.unlink()
         pick.do_transfer()
Exemplo n.º 27
0
    def _put_in_pack_custom(self):
        package = False
        for pick in self.filtered(lambda p: p.state not in ('done', 'cancel')):
            move_line_ids = pick.move_line_ids_without_package.filtered(lambda o: not o.result_package_id)
            if move_line_ids : #or move_line_ids_2
                move_lines_to_pack = self.env['stock.move.line']
                package = self.env['stock.quant.package'].create({})
                for ml in move_line_ids:
                    if float_compare(ml.qty_done, ml.product_uom_qty,
                                     precision_rounding=ml.product_uom_id.rounding) >= 0:
                        move_lines_to_pack |= ml
                    else:
                        quantity_left_todo = float_round(
                            ml.product_uom_qty - ml.qty_done,
                            precision_rounding=ml.product_uom_id.rounding,
                            rounding_method='UP')
                        done_to_keep = ml.qty_done
                        new_move_line = ml.copy(
                            default={'product_uom_qty': 0, 'qty_done': ml.qty_done})
                        ml.write({'product_uom_qty': quantity_left_todo, 'qty_done': 0.0})
                        new_move_line.write({'product_uom_qty': done_to_keep})
                        move_lines_to_pack |= new_move_line

                package_level = self.env['stock.package_level'].create({
                    'package_id': package.id,
                    'picking_id': pick.id,
                    'location_id': False,
                    'location_dest_id': move_line_ids.mapped('location_dest_id').id,
                    'move_line_ids': [(6, 0, move_lines_to_pack.ids)]
                })
                move_lines_to_pack.write({
                    'result_package_id': package.id,
                })
            else:
                raise UserError(_('You must first set the quantity you will put in the pack.'))
        return package.id
Exemplo n.º 28
0
 def _compute_total_display_difference(self):
     for invoice in self:
         invoice.check_total_display_difference = float_round(
             invoice.check_total - invoice.amount_total,
             precision_rounding=invoice.currency_id.rounding)
Exemplo n.º 29
0
def get_a_field_val(
        self,
        field_name,
        field_attr,
        needdata,
        row,
        sheet,
        check_file,
        sheet_of_copy_wb,
        merge_tuple_list,
        model_name,
        noti_dict,
        key_search_dict,
        update_dict,
        #                                    x2m_fields,
        collection_dict,
        setting,
        sheet_of_copy_wb_para):
    skip_this_field = get_key(field_attr, 'skip_this_field', False)
    if callable(skip_this_field):
        skip_this_field = skip_this_field(self)
    if skip_this_field:
        return 'continue'
    col_index = get_key(field_attr, 'col_index')
    func = get_key(field_attr, 'func')
    #F11
    obj, val = read_val_for_ci(self,
                               field_attr,
                               check_file,
                               needdata,
                               noti_dict,
                               setting,
                               excel_para={
                                   'col_index': col_index,
                                   'sheet': sheet,
                                   'row': row,
                                   'merge_tuple_list': merge_tuple_list,
                                   'sheet_of_copy_wb': sheet_of_copy_wb
                               },
                               for_print_para={
                                   'model_name': model_name,
                                   'field_name': field_name
                               },
                               sheet_of_copy_wb_para=sheet_of_copy_wb_para)

    field_attr['before_func_val'] = val
    # func
    karg = get_key(field_attr, 'karg', {})
    if karg == None:
        karg = {}
    func_pre_func = field_attr.get('func_pre_func')
    if func_pre_func:
        val = func_pre_func(val, needdata, self)
    if func:
        try:
            val = func(val, needdata, **karg)
        except TypeError:
            try:
                val = func(val, needdata, self, **karg)
            except TypeError:
                val = func(val, **karg)
#         print ('func read model_name:%s field_name:%s'%(model_name,field_name),'val',val)

    val = replace_val_for_ci(field_attr, val, needdata)
    field_attr['val_goc'] = val
    if val == False:
        default_val = field_attr.get('default_val')
        if default_val != None:
            val = default_val
    if field_attr.get('field_type') == 'float':
        val = float_round(val, precision_rounding=0.01)

    field_attr['val'] = val
    field_attr['obj'] = obj
    if check_file:
        required_when_normal = get_key(field_attr, 'required', False)
        required = get_key(field_attr, 'required_when_check_file',
                           required_when_normal)
        if (required_when_normal and val == False) and required == False:
            collection_dict['instance_false'] = True
    else:
        required = get_key(field_attr, 'required', False)

    key_or_not = field_attr.get('key')
    if '2many' in field_attr.get('field_type', '') and val == False:
        a_field_code = 'continue'
        return a_field_code
    if required and (val == False and isinstance(
            val, bool)):  # val ==False <==> val ==0, val ==0 <==> val =False
        this_model_notice = noti_dict.setdefault(model_name, {})
        skip_because_required = this_model_notice.setdefault(
            'skip_because_required', 0)
        this_model_notice['skip_because_required'] = skip_because_required + 1
        a_field_code = 'break_out_a_row_because_a_required'
        return a_field_code  #sua 5
    elif not field_attr.get('for_excel_readonly'):
        if key_or_not == True:
            key_search_dict[field_name] = val
        elif key_or_not == 'Both':
            key_search_dict[field_name] = val
            update_dict[field_name] = val
        else:
            update_dict[field_name] = val
    valid_field_func = field_attr.get('valid_field_func')
    if valid_field_func:
        valid_field_func(val, obj, needdata, self)
    print("row: ", row, 'model_name: ', model_name, '-field: ', field_name,
          '-val: ', val)
    check_type_of_val(field_attr, val, field_name, model_name)
    a_field_code = False
    return a_field_code
Exemplo n.º 30
0
 def _compute_mrp_product_qty(self):
     for template in self:
         template.mrp_product_qty = float_round(sum(template.mapped('product_variant_ids').mapped('mrp_product_qty')), precision_rounding=template.uom_id.rounding)
Exemplo n.º 31
0
 def onchange_check_total(self):
     self.check_total_display_difference = float_round(
         self.check_total - self.amount_total,
         precision_rounding=self.currency_id.rounding)
Exemplo n.º 32
0
    def _stripe_create_payment_intent(self, acquirer_ref=None, email=None):
        if not self.payment_token_id.stripe_payment_method:
            # old token before using sca, need to fetch data from the api
            self.payment_token_id._stripe_sca_migrate_customer()

        charge_params = {
            'amount': int(self.amount if self.currency_id.name in INT_CURRENCIES else float_round(self.amount * 100, 2)),
            'currency': self.currency_id.name.lower(),
            'off_session': True,
            'confirm': True,
            'payment_method': self.payment_token_id.stripe_payment_method,
            'customer': self.payment_token_id.acquirer_ref,
            "description": self.reference,
        }
        if not self.env.context.get('off_session'):
            charge_params.update(setup_future_usage='off_session', off_session=False)
        _logger.info('_stripe_create_payment_intent: Sending values to stripe, values:\n%s', pprint.pformat(charge_params))

        res = self.acquirer_id._stripe_request('payment_intents', charge_params)
        if res.get('charges') and res.get('charges').get('total_count'):
            res = res.get('charges').get('data')[0]

        _logger.info('_stripe_create_payment_intent: Values received:\n%s', pprint.pformat(res))
        return res
Exemplo n.º 33
0
    def _compute_years_in(self):
        for me in self:
            if me.employee_id:
                contract_id = me.env["hr.contract"].search([
                    ("employee_id", "=", me.employee_id.id),
                    ("date_start", "<=", me.date_stop),
                    ("date_end", ">=", me.date_stop),
                ])
                remaining = me.employee_id._get_paid_remaining_leaves()
                me.days_paid_holidays = float_round(remaining.get(
                    me.employee_id.id, 0.0),
                                                    precision_digits=2)
                difference_in_years = relativedelta(me.date_stop, me.date_in)
                me.years_in = difference_in_years.years
                me.months_in = difference_in_years.months
                me.days_in = difference_in_years.days
                if contract_id:
                    if contract_id.structure_type_id.is_allow:
                        amount_res = (contract_id[0].wage +
                                      contract_id[0].amount_hous +
                                      contract_id[0].amount_trasportation +
                                      contract_id[0].amount_anuther_allow)
                    else:
                        amount_res = (contract_id[0].wage +
                                      contract_id[0].amount_anuther_allow)

                    me.amount_days_paid_holidays = round(
                        (amount_res / 30) * me.days_paid_holidays, 2)
                else:
                    me.amount_days_paid_holidays = 0.0

                if me.type_end == 'Resignation':
                    me.amount_in_days = ((me.years_in * 365) +
                                         (me.months_in * 30) +
                                         (me.days_in)) / 365
                    try:
                        if me.years_in < 2:
                            me.sum_lastes = 0.0
                        elif me.years_in >= 2 and me.years_in < 5:
                            me.sum_lastes = (33.33 /
                                             100) * me.amount_in_days * (
                                                 amount_res / 2)
                        elif me.years_in >= 5 and me.years_in < 10:
                            res11 = (66.66 / 100) * 5 * (amount_res / 2)
                            res12 = (66.66 / 100) * (me.amount_in_days -
                                                     5) * (amount_res)
                            me.sum_lastes = res11 + res12
                        elif me.years_in >= 10:
                            res1 = 5 * (amount_res / 2)
                            res3 = (me.amount_in_days - 5) * (amount_res)
                            me.sum_lastes = res1 + res3
                    except:
                        me.sum_lastes = 0.0

                else:
                    me.amount_in_days = ((me.years_in * 365) +
                                         (me.months_in * 30) +
                                         (me.days_in)) / 365

                    if me.amount_in_days <= 0.24:
                        me.sum_lastes = 0.0
                    elif me.amount_in_days > 0.24 and me.amount_in_days <= 5:
                        try:
                            res3 = me.amount_in_days * ((amount_res) / 2)
                            me.sum_lastes = res3
                        except:
                            me.sum_lastes = 0.0
                    elif me.amount_in_days > 5:
                        try:
                            res3 = 5 * ((amount_res) / 2)
                            res32 = (me.amount_in_days - 5) * (amount_res)
                            me.sum_lastes = res3 + res32
                        except:
                            me.sum_lastes = 0.0

            accounts = []
            monthly = me.env["advanced.salary.monthly"].search([
                ("hr_employee", "=", me.employee_id.id)
            ])
            sum_monthly = 0.0
            for m in monthly:
                if m.account_id.id in accounts:
                    continue
                else:
                    accounts.append(m.account_id.id)
                    sum_monthly += m.balance
            me.balance = sum_monthly
            me.net = me.sum_lastes + me.amount_days_paid_holidays - sum_monthly
Exemplo n.º 34
0
 def _compute_mrp_product_qty(self):
     self.mrp_product_qty = float_round(
         sum(self.mapped('product_variant_ids').mapped('mrp_product_qty')),
         precision_rounding=self.uom_id.rounding)
Exemplo n.º 35
0
    def stripe_form_generate_values(self, tx_values):
        self.ensure_one()

        base_url = self.get_base_url()
        stripe_session_data = {
            'line_items[][amount]': int(tx_values['amount'] if tx_values['currency'].name in INT_CURRENCIES else float_round(tx_values['amount'] * 100, 2)),
            'line_items[][currency]': tx_values['currency'].name,
            'line_items[][quantity]': 1,
            'line_items[][name]': tx_values['reference'],
            'client_reference_id': tx_values['reference'],
            'success_url': urls.url_join(base_url, StripeController._success_url) + '?reference=%s' % tx_values['reference'],
            'cancel_url': urls.url_join(base_url, StripeController._cancel_url) + '?reference=%s' % tx_values['reference'],
            'payment_intent_data[description]': tx_values['reference'],
            'customer_email': tx_values.get('partner_email') or tx_values.get('billing_partner_email'),
        }

        self._add_available_payment_method_types(stripe_session_data, tx_values)

        tx_values['session_id'] = self.with_context(stripe_manual_payment=True)._create_stripe_session(stripe_session_data)

        return tx_values
Exemplo n.º 36
0
    def _action_done(self):
        """ This method is called during a move's `action_done`. It'll actually move a quant from
        the source location to the destination location, and unreserve if needed in the source
        location.

        This method is intended to be called on all the move lines of a move. This method is not
        intended to be called when editing a `done` move (that's what the override of `write` here
        is done.
        """
        Quant = self.env['stock.quant']

        # First, we loop over all the move lines to do a preliminary check: `qty_done` should not
        # be negative and, according to the presence of a picking type or a linked inventory
        # adjustment, enforce some rules on the `lot_id` field. If `qty_done` is null, we unlink
        # the line. It is mandatory in order to free the reservation and correctly apply
        # `action_done` on the next move lines.
        ml_to_delete = self.env['stock.move.line']
        for ml in self:
            # Check here if `ml.qty_done` respects the rounding of `ml.product_uom_id`.
            uom_qty = float_round(
                ml.qty_done,
                precision_rounding=ml.product_uom_id.rounding,
                rounding_method='HALF-UP')
            precision_digits = self.env['decimal.precision'].precision_get(
                'Product Unit of Measure')
            qty_done = float_round(ml.qty_done,
                                   precision_digits=precision_digits,
                                   rounding_method='HALF-UP')
            if float_compare(
                    uom_qty, qty_done, precision_digits=precision_digits) != 0:
                raise UserError(
                    _('The quantity done for the product "%s" doesn\'t respect the rounding precision \
                                  defined on the unit of measure "%s". Please change the quantity done or the \
                                  rounding precision of your unit of measure.')
                    % (ml.product_id.display_name, ml.product_uom_id.name))

            qty_done_float_compared = float_compare(
                ml.qty_done, 0, precision_rounding=ml.product_uom_id.rounding)
            if qty_done_float_compared > 0:
                if ml.product_id.tracking != 'none':
                    picking_type_id = ml.move_id.picking_type_id
                    if picking_type_id:
                        if picking_type_id.use_create_lots:
                            # If a picking type is linked, we may have to create a production lot on
                            # the fly before assigning it to the move line if the user checked both
                            # `use_create_lots` and `use_existing_lots`.
                            ml._create_and_assign_production_lot()
                        elif not picking_type_id.use_create_lots and not picking_type_id.use_existing_lots:
                            # If the user disabled both `use_create_lots` and `use_existing_lots`
                            # checkboxes on the picking type, he's allowed to enter tracked
                            # products without a `lot_id`.
                            continue
                    elif ml.move_id.inventory_id:
                        # If an inventory adjustment is linked, the user is allowed to enter
                        # tracked products without a `lot_id`.
                        continue

                    if not ml.lot_id:
                        raise UserError(
                            _('You need to supply a Lot/Serial number for product %s.'
                              ) % ml.product_id.display_name)
            elif qty_done_float_compared < 0:
                raise UserError(_('No negative quantities allowed'))
            else:
                ml_to_delete |= ml
        ml_to_delete.unlink()

        (self - ml_to_delete)._check_company()

        # Now, we can actually move the quant.
        done_ml = self.env['stock.move.line']
        for ml in self - ml_to_delete:
            if ml.product_id.type == 'product':
                rounding = ml.product_uom_id.rounding

                # if this move line is force assigned, unreserve elsewhere if needed
                if not ml._should_bypass_reservation(
                        ml.location_id) and float_compare(
                            ml.qty_done,
                            ml.product_qty,
                            precision_rounding=rounding) > 0:
                    extra_qty = ml.qty_done - ml.product_qty
                    ml._free_reservation(ml.product_id,
                                         ml.location_id,
                                         extra_qty,
                                         lot_id=ml.lot_id,
                                         package_id=ml.package_id,
                                         owner_id=ml.owner_id,
                                         ml_to_ignore=done_ml)
                # unreserve what's been reserved
                if not ml._should_bypass_reservation(
                        ml.location_id
                ) and ml.product_id.type == 'product' and ml.product_qty:
                    try:
                        Quant._update_reserved_quantity(
                            ml.product_id,
                            ml.location_id,
                            -ml.product_qty,
                            lot_id=ml.lot_id,
                            package_id=ml.package_id,
                            owner_id=ml.owner_id,
                            strict=True)
                    except UserError:
                        Quant._update_reserved_quantity(
                            ml.product_id,
                            ml.location_id,
                            -ml.product_qty,
                            lot_id=False,
                            package_id=ml.package_id,
                            owner_id=ml.owner_id,
                            strict=True)

                # move what's been actually done
                quantity = ml.product_uom_id._compute_quantity(
                    ml.qty_done,
                    ml.move_id.product_id.uom_id,
                    rounding_method='HALF-UP')
                available_qty, in_date = Quant._update_available_quantity(
                    ml.product_id,
                    ml.location_id,
                    -quantity,
                    lot_id=ml.lot_id,
                    package_id=ml.package_id,
                    owner_id=ml.owner_id)
                if available_qty < 0 and ml.lot_id:
                    # see if we can compensate the negative quants with some untracked quants
                    untracked_qty = Quant._get_available_quantity(
                        ml.product_id,
                        ml.location_id,
                        lot_id=False,
                        package_id=ml.package_id,
                        owner_id=ml.owner_id,
                        strict=True)
                    if untracked_qty:
                        taken_from_untracked_qty = min(untracked_qty,
                                                       abs(quantity))
                        Quant._update_available_quantity(
                            ml.product_id,
                            ml.location_id,
                            -taken_from_untracked_qty,
                            lot_id=False,
                            package_id=ml.package_id,
                            owner_id=ml.owner_id)
                        Quant._update_available_quantity(
                            ml.product_id,
                            ml.location_id,
                            taken_from_untracked_qty,
                            lot_id=ml.lot_id,
                            package_id=ml.package_id,
                            owner_id=ml.owner_id)
                Quant._update_available_quantity(
                    ml.product_id,
                    ml.location_dest_id,
                    quantity,
                    lot_id=ml.lot_id,
                    package_id=ml.result_package_id,
                    owner_id=ml.owner_id,
                    in_date=in_date)
            done_ml |= ml
        # Reset the reserved quantity as we just moved it to the destination location.
        (self -
         ml_to_delete).with_context(bypass_reservation_update=True).write({
             'product_uom_qty':
             0.00,
             'date':
             fields.Datetime.now(),
         })
    def _compute_quantities_product_quant_dic(self, lot_id, owner_id,
                                              package_id, from_date, to_date,
                                              product_obj, data):

        loc_list = []

        domain_quant_loc, domain_move_in_loc, domain_move_out_loc = product_obj._get_domain_locations(
        )
        custom_domain = []
        if data['company_id']:
            obj = self.env['res.company'].search([('name', '=',
                                                   data['company_id'])])

            custom_domain.append(('company_id', '=', obj.id))

        if data['location_id']:
            custom_domain.append(('location_id', '=', data['location_id'].id))

        if data['warehouse']:
            ware_check_domain = [a.id for a in data['warehouse']]
            locations = []
            for i in ware_check_domain:

                loc_ids = self.env['stock.warehouse'].search([('id', '=', i)])

                locations.append(loc_ids.view_location_id.id)
                for i in loc_ids.view_location_id.child_ids:
                    locations.append(i.id)

                loc_list.append(loc_ids.lot_stock_id.id)

            custom_domain.append(('location_id', 'in', locations))

        domain_quant = [('product_id', 'in', product_obj.ids)
                        ] + domain_quant_loc + custom_domain
        #print ("dddddddddddddddddddddddddddddddddddddddddd",domain_quant)
        dates_in_the_past = False
        # only to_date as to_date will correspond to qty_available
        #to_date = fields.Datetime.to_datetime(to_date)

        if to_date and to_date < date.today():

            dates_in_the_past = True

        domain_move_in = [('product_id', 'in', product_obj.ids)
                          ] + domain_move_in_loc
        domain_move_out = [('product_id', 'in', product_obj.ids)
                           ] + domain_move_out_loc
        if lot_id is not None:
            domain_quant += [('lot_id', '=', lot_id)]
        if owner_id is not None:
            domain_quant += [('owner_id', '=', owner_id)]
            domain_move_in += [('restrict_partner_id', '=', owner_id)]
            domain_move_out += [('restrict_partner_id', '=', owner_id)]
        if package_id is not None:
            domain_quant += [('package_id', '=', package_id)]
        if dates_in_the_past:
            domain_move_in_done = list(domain_move_in)
            domain_move_out_done = list(domain_move_out)
        if from_date:
            domain_move_in += [('date', '>=', from_date)]
            domain_move_out += [('date', '>=', from_date)]
        if to_date:
            domain_move_in += [('date', '<=', to_date)]
            domain_move_out += [('date', '<=', to_date)]

        Move = self.env['stock.move']
        Quant = self.env['stock.quant']
        domain_move_in_todo = [
            ('state', 'in',
             ('waiting', 'confirmed', 'assigned', 'partially_available'))
        ] + domain_move_in
        domain_move_out_todo = [
            ('state', 'in',
             ('waiting', 'confirmed', 'assigned', 'partially_available'))
        ] + domain_move_out
        moves_in_res = dict((item['product_id'][0], item['product_qty'])
                            for item in Move.read_group(
                                domain_move_in_todo,
                                ['product_id', 'product_qty'], ['product_id'],
                                orderby='id'))
        moves_out_res = dict((item['product_id'][0], item['product_qty'])
                             for item in Move.read_group(
                                 domain_move_out_todo,
                                 ['product_id', 'product_qty'], ['product_id'],
                                 orderby='id'))
        quants_res = dict(
            (item['product_id'][0], item['quantity'])
            for item in Quant.read_group(
                domain_quant, ['product_id', 'quantity'], ['product_id'],
                orderby='id'))

        if dates_in_the_past:
            # Calculate the moves that were done before now to calculate back in time (as most questions will be recent ones)
            domain_move_in_done = [('state', '=', 'done'),
                                   ('date', '>', to_date)
                                   ] + domain_move_in_done
            domain_move_out_done = [('state', '=', 'done'),
                                    ('date', '>', to_date)
                                    ] + domain_move_out_done
            moves_in_res_past = dict(
                (item['product_id'][0], item['product_qty']) for item in
                Move.read_group(domain_move_in_done,
                                ['product_id', 'product_qty'], ['product_id'],
                                orderby='id'))
            moves_out_res_past = dict(
                (item['product_id'][0], item['product_qty']) for item in
                Move.read_group(domain_move_out_done,
                                ['product_id', 'product_qty'], ['product_id'],
                                orderby='id'))

        res = dict()
        for product in product_obj.with_context(prefetch_fields=False):
            product_id = product.id
            rounding = product.uom_id.rounding
            res[product_id] = {}
            if dates_in_the_past:
                qty_available = quants_res.get(
                    product_id, 0.0) - moves_in_res_past.get(
                        product_id, 0.0) + moves_out_res_past.get(
                            product_id, 0.0)
            else:
                qty_available = quants_res.get(product_id, 0.0)
            res[product_id]['qty_available'] = float_round(
                qty_available, precision_rounding=rounding)
            res[product_id]['incoming_qty'] = float_round(
                moves_in_res.get(product_id, 0.0), precision_rounding=rounding)
            res[product_id]['outgoing_qty'] = float_round(
                moves_out_res.get(product_id, 0.0),
                precision_rounding=rounding)
            res[product_id]['virtual_available'] = float_round(
                qty_available + res[product_id]['incoming_qty'] -
                res[product_id]['outgoing_qty'],
                precision_rounding=rounding)

        return res
Exemplo n.º 38
0
from odoo.tools import float_utils

float_utils.float_round(1.5424, precision_digits=3, rounding_method='DOWN')
# 1.542

float_utils.float_round(1.5424, precision_digits=3, rounding_method='UP')

float_utils.float_round(1.5424, precision_digits=3, rounding_method='HALF-UP')
# 1.542

float_utils.float_round(49, precision_rounding=100, rounding_method='HALF-UP')
# 0.0

float_utils.float_round(50, precision_rounding=100, rounding_method='HALF-UP')
# 100.0

float_utils.float_is_zero(0.04252, precision_digits=5)
# False
float_utils.float_is_zero(0.04252, precision_digits=1)
# True

float_utils.float_compare(0.042555, 0.04256, precision_digits=5)
# 0 => Равны

float_utils.float_compare(0.042555, 0.04256, precision_digits=6)
# -1 => Первое меньше второго

float_utils.float_compare(0.04256, 0.042555, precision_digits=6)
# 1 => Первое больше второго
Exemplo n.º 39
0
    def test_01_compute_price_operation_cost(self):
        """Test calcuation of bom cost with operations."""
        workcenter_from1 = Form(self.env['mrp.workcenter'])
        workcenter_from1.name = 'Workcenter'
        workcenter_from1.time_efficiency = 100
        workcenter_from1.capacity = 2
        workcenter_from1.oee_target = 100
        workcenter_from1.time_start = 0
        workcenter_from1.time_stop = 0
        workcenter_from1.costs_hour = 100
        workcenter_1 = workcenter_from1.save()

        routing_form1 = Form(self.Routing)
        routing_form1.name = 'Assembly Furniture'
        routing_1 = routing_form1.save()

        operation_1 = self.operation.create({
            'name': 'Cutting',
            'workcenter_id': workcenter_1.id,
            'routing_id': routing_1.id,
            'time_mode': 'manual',
            'time_cycle_manual': 20,
            'batch': 'no',
            'sequence': 1,
        })
        operation_2 = self.operation.create({
            'name': 'Drilling',
            'workcenter_id': workcenter_1.id,
            'routing_id': routing_1.id,
            'time_mode': 'manual',
            'time_cycle_manual': 25,
            'batch': 'no',
            'sequence': 2,
        })
        operation_3 = self.operation.create({
            'name': 'Fitting',
            'workcenter_id': workcenter_1.id,
            'routing_id': routing_1.id,
            'time_mode': 'manual',
            'time_cycle_manual': 30,
            'batch': 'no',
            'sequence': 3,
        })

        # -----------------------------------------------------------------
        # Dinning Table Operation Cost(1 Unit)
        # -----------------------------------------------------------------
        # Operation cost calculate for 1 units
        # Cutting        (20 / 60) * 100 =  33.33
        # Drilling       (25 / 60) * 100 =  41.67
        # Fitting        (30 / 60) * 100 =  50.00
        # ----------------------------------------
        # Operation Cost  1 unit = 125
        # -----------------------------------------------------------------

        self.bom_1.routing_id = routing_1.id

        # --------------------------------------------------------------------------
        # Table Head Operation Cost (1 Dozen)
        # --------------------------------------------------------------------------
        # Operation cost calculate for 1 dozens
        # Cutting        (20 * 1 / 60) * 100 =  33,33
        # Drilling       (25 * 1 / 60) * 100 =  41,67
        # Fitting        (30 * 1 / 60) * 100 =  50
        # ----------------------------------------
        # Operation Cost 1 dozen (125 per dozen) and 10.42 for 1 Unit
        # --------------------------------------------------------------------------

        self.bom_2.routing_id = routing_1.id

        self.assertEqual(self.dining_table.standard_price, 1000,
                         "Initial price of the Product should be 1000")
        self.dining_table.button_bom_cost()
        # Total cost of Dining Table = (550) + Total cost of operations (125) = 675.0
        self.assertEqual(
            float_round(self.dining_table.standard_price, precision_digits=2),
            675.0, "After computing price from BoM price should be 612.5")
        self.Product.browse([self.dining_table.id,
                             self.table_head.id]).action_bom_cost()
        # Total cost of Dining Table = (718.75) + Total cost of all operations (125 + 10.42) = 854.17
        self.assertEqual(
            float_compare(self.dining_table.standard_price,
                          854.17,
                          precision_digits=2), 0,
            "After computing price from BoM price should be 786.46")
Exemplo n.º 40
0
 def _onchange_hours_per_day(self):
     attendances = self.attendance_ids.filtered(lambda attendance: not attendance.date_from and not attendance.date_to)
     hour_count = 0.0
     for attendance in attendances:
         hour_count += attendance.hour_to - attendance.hour_from
     self.hours_per_day = float_round(hour_count / float(len(set(attendances.mapped('dayofweek')))), precision_digits=2)
Exemplo n.º 41
0
    def _quant_create_from_move(self,
                                qty,
                                move,
                                lot_id=False,
                                owner_id=False,
                                src_package_id=False,
                                dest_package_id=False,
                                force_location_from=False,
                                force_location_to=False):
        '''Create a quant in the destination location and create a negative
        quant in the source location if it's an internal location. '''
        price_unit = move.get_price_unit()
        location = force_location_to or move.location_dest_id
        rounding = move.product_id.uom_id.rounding
        vals = {
            'product_id': move.product_id.id,
            'location_id': location.id,
            'qty': float_round(qty, precision_rounding=rounding),
            'cost': price_unit,
            'history_ids': [(4, move.id)],
            'in_date': datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT),
            'company_id': move.company_id.id,
            'lot_id': lot_id,
            'owner_id': owner_id,
            'package_id': dest_package_id,
        }
        if move.location_id.usage == 'internal':
            # if we were trying to move something from an internal location and reach here (quant creation),
            # it means that a negative quant has to be created as well.
            negative_vals = vals.copy()
            negative_vals[
                'location_id'] = force_location_from and force_location_from.id or move.location_id.id
            negative_vals['qty'] = float_round(-qty,
                                               precision_rounding=rounding)
            negative_vals['cost'] = price_unit
            negative_vals['negative_move_id'] = move.id
            negative_vals['package_id'] = src_package_id
            negative_quant_id = self.sudo().create(negative_vals)
            vals.update({'propagated_from_id': negative_quant_id.id})

        # In case of serial tracking, check if the product does not exist somewhere internally already
        picking_type = move.picking_id and move.picking_id.picking_type_id or False
        if lot_id and move.product_id.tracking == 'serial' and (
                not picking_type or
            (picking_type.use_create_lots or picking_type.use_existing_lots)):
            if qty != 1.0:
                raise UserError(
                    _('You should only receive by the piece with the same serial number'
                      ))
            other_quants = self.search([
                ('product_id', '=', move.product_id.id),
                ('lot_id', '=', lot_id), ('qty', '>', 0.0),
                ('location_id.usage', '=', 'internal')
            ])
            if other_quants:
                lot_name = self.env['stock.production.lot'].browse(lot_id).name
                raise UserError(
                    _('The serial number %s is already in stock.') % lot_name +
                    _("Otherwise make sure the right stock/owner is set."))

        # create the quant as superuser, because we want to restrict the creation of quant manually: we should always use this method to create quants
        return self.sudo().create(vals)
Exemplo n.º 42
0
 def _get_formatted_amount(self):
     amount = self.collection._get_amount_to_capture()
     if self.collection.currency_id.name in ZERO_DECIMAL_CURRENCIES:
         return int(amount)
     else:
         return int(float_round(amount * 100, 0))
Exemplo n.º 43
0
    def _quant_create(self,
                      cr,
                      uid,
                      qty,
                      move,
                      lot_id=False,
                      owner_id=False,
                      src_package_id=False,
                      dest_package_id=False,
                      force_location_from=False,
                      force_location_to=False,
                      context=None):
        '''Create a quant in the destination location and create a negative quant in the source location if it's an internal location.
        '''
        if context is None:
            context = {}
        price_unit = self.pool.get('stock.move').get_price_unit(
            cr, uid, move, context=context)
        location = force_location_to or move.location_dest_id
        rounding = move.product_id.uom_id.rounding
        vals = {
            'product_id': move.product_id.id,
            'location_id': location.id,
            'qty': float_round(qty, precision_rounding=rounding),
            'cost': price_unit,
            'history_ids': [(4, move.id)],
            'in_date': datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT),
            'company_id': move.company_id.id,
            'lot_id': lot_id,
            'owner_id': owner_id,
            'package_id': dest_package_id,
            'description': move.name,
            'price_unit': move.price_unit,
            'currency_id': move.currency_id.id,
            'quant_line_tax_ids': [(6, 0, move.move_line_tax_ids.ids)],
        }
        if move.location_id.usage == 'internal':
            #if we were trying to move something from an internal location and reach here (quant creation),
            #it means that a negative quant has to be created as well.
            negative_vals = vals.copy()
            negative_vals[
                'location_id'] = force_location_from and force_location_from.id or move.location_id.id
            negative_vals['qty'] = float_round(-qty,
                                               precision_rounding=rounding)
            negative_vals['cost'] = price_unit
            negative_vals['negative_move_id'] = move.id
            negative_vals['package_id'] = src_package_id
            negative_quant_id = self.create(cr,
                                            SUPERUSER_ID,
                                            negative_vals,
                                            context=context)
            vals.update({'propagated_from_id': negative_quant_id})

        picking_type = move.picking_id and move.picking_id.picking_type_id or False
        if lot_id and move.product_id.tracking == 'serial' and (
                not picking_type or
            (picking_type.use_create_lots or picking_type.use_existing_lots)):
            if qty != 1.0:
                raise UserError(
                    _('You should only receive by the piece with the same serial number'
                      ))

        #create the quant as superuser, because we want to restrict the creation of quant manually: we should always use this method to create quants
        quant_id = self.create(cr, SUPERUSER_ID, vals, context=context)
        return self.browse(cr, uid, quant_id, context=context)
Exemplo n.º 44
0
 def _compute_sale_total(self):
     for template in self:
         template.sales_count = float_round(
             sum([p.sales_count for p in template.product_variant_ids]),
             precision_rounding=template.uom_id.rounding)
Exemplo n.º 45
0
 def _stripe_form_get_invalid_parameters(self, data):
     invalid_parameters = []
     if data.get('amount') != int(self.amount if self.currency_id.name in INT_CURRENCIES else float_round(self.amount * 100, 2)):
         invalid_parameters.append(('Amount', data.get('amount'), self.amount * 100))
     if data.get('currency').upper() != self.currency_id.name:
         invalid_parameters.append(('Currency', data.get('currency'), self.currency_id.name))
     if data.get('payment_intent') and data.get('payment_intent') != self.stripe_payment_intent:
         invalid_parameters.append(('Payment Intent', data.get('payment_intent'), self.stripe_payment_intent))
     return invalid_parameters
Exemplo n.º 46
0
    def _compute_quantities_dict(self,
                                 lot_id,
                                 owner_id,
                                 package_id,
                                 from_date=False,
                                 to_date=False):
        domain_quant_loc, domain_move_in_loc, domain_move_out_loc = self._get_domain_locations(
        )
        domain_quant = [('product_id', 'in', self.ids)] + domain_quant_loc
        dates_in_the_past = False
        # only to_date as to_date will correspond to qty_available
        to_date = fields.Datetime.to_datetime(to_date)
        if to_date and to_date < fields.Datetime.now():
            dates_in_the_past = True

        domain_move_in = [('product_id', 'in', self.ids)] + domain_move_in_loc
        domain_move_out = [('product_id', 'in', self.ids)
                           ] + domain_move_out_loc
        if lot_id is not None:
            domain_quant += [('lot_id', '=', lot_id)]
        if owner_id is not None:
            domain_quant += [('owner_id', '=', owner_id)]
            domain_move_in += [('restrict_partner_id', '=', owner_id)]
            domain_move_out += [('restrict_partner_id', '=', owner_id)]
        if package_id is not None:
            domain_quant += [('package_id', '=', package_id)]
        if dates_in_the_past:
            domain_move_in_done = list(domain_move_in)
            domain_move_out_done = list(domain_move_out)
        if from_date:
            domain_move_in += [('date', '>=', from_date)]
            domain_move_out += [('date', '>=', from_date)]
        if to_date:
            domain_move_in += [('date', '<=', to_date)]
            domain_move_out += [('date', '<=', to_date)]

        Move = self.env['stock.move']
        Quant = self.env['stock.quant']
        domain_move_in_todo = [
            ('state', 'in',
             ('waiting', 'confirmed', 'assigned', 'partially_available'))
        ] + domain_move_in
        domain_move_out_todo = [
            ('state', 'in',
             ('waiting', 'confirmed', 'assigned', 'partially_available'))
        ] + domain_move_out
        moves_in_res = dict((item['product_id'][0], item['product_qty'])
                            for item in Move.read_group(
                                domain_move_in_todo,
                                ['product_id', 'product_qty'], ['product_id'],
                                orderby='id'))
        moves_out_res = dict((item['product_id'][0], item['product_qty'])
                             for item in Move.read_group(
                                 domain_move_out_todo,
                                 ['product_id', 'product_qty'], ['product_id'],
                                 orderby='id'))
        quants_res = dict(
            (item['product_id'][0], item['quantity'])
            for item in Quant.read_group(
                domain_quant, ['product_id', 'quantity'], ['product_id'],
                orderby='id'))
        if dates_in_the_past:
            # Calculate the moves that were done before now to calculate back in time (as most questions will be recent ones)
            domain_move_in_done = [('state', '=', 'done'),
                                   ('date', '>', to_date)
                                   ] + domain_move_in_done
            domain_move_out_done = [('state', '=', 'done'),
                                    ('date', '>', to_date)
                                    ] + domain_move_out_done
            moves_in_res_past = dict(
                (item['product_id'][0], item['product_qty']) for item in
                Move.read_group(domain_move_in_done,
                                ['product_id', 'product_qty'], ['product_id'],
                                orderby='id'))
            moves_out_res_past = dict(
                (item['product_id'][0], item['product_qty']) for item in
                Move.read_group(domain_move_out_done,
                                ['product_id', 'product_qty'], ['product_id'],
                                orderby='id'))

        res = dict()
        for product in self.with_context(prefetch_fields=False):
            product_id = product.id
            rounding = product.uom_id.rounding
            res[product_id] = {}
            if dates_in_the_past:
                qty_available = quants_res.get(
                    product_id, 0.0) - moves_in_res_past.get(
                        product_id, 0.0) + moves_out_res_past.get(
                            product_id, 0.0)
            else:
                qty_available = quants_res.get(product_id, 0.0)
            res[product_id]['qty_available'] = float_round(
                qty_available, precision_rounding=rounding)
            res[product_id]['incoming_qty'] = float_round(
                moves_in_res.get(product_id, 0.0), precision_rounding=rounding)
            res[product_id]['outgoing_qty'] = float_round(
                moves_out_res.get(product_id, 0.0),
                precision_rounding=rounding)
            res[product_id]['virtual_available'] = float_round(
                qty_available + res[product_id]['incoming_qty'] -
                res[product_id]['outgoing_qty'],
                precision_rounding=rounding)

        return res
Exemplo n.º 47
0
 def default_get(self, fields_list):
     res = super(ReturnPicking, self).default_get(fields_list)
     if res.get('picking_id') and res.get('product_return_moves'):
         product_return_moves = []
         picking = self.env['stock.picking'].browse(res['picking_id'])
         if picking:
             res.update({
                 'owner_id':
                 picking.owner_id and picking.owner_id.id or False,
             })
             res_move_id = set([])
             quantity_new = {}
             for move_line in res['product_return_moves']:
                 # _logger.info("product return moves %s" % (move_line,))
                 cmd, arg, value = move_line
                 res_move_id.update([value['move_id']])
                 quantity_new[value['move_id']] = value['quantity']
             move_lines = self.env['stock.move'].browse(list(res_move_id))
             for move in move_lines:
                 del_move_lines = self.env['stock.move.line']
                 quantitys = {}
                 if move.state == 'cancel':
                     continue
                 if move.scrapped:
                     continue
                 current_move_lines = move.move_line_ids.filtered(
                     lambda m: m.state in
                     ['partially_available', 'assigned', 'done'])
                 quantity_save = quantity_new[move.id]
                 for move_line in current_move_lines:
                     returned_ids = picking.mapped(
                         'move_lines.returned_move_ids.move_line_ids'
                     ).filtered(lambda r: r.lot_id == move_line.lot_id)
                     returned_quantity = sum(
                         [x.qty_done for x in returned_ids])
                     if move_line.qty_done == returned_quantity:
                         del_move_lines = move_line
                         continue
                     quantitys[
                         move_line] = move_line.qty_done - returned_quantity
                     # for returned in returned_ids:
                     #     _logger.info("RETURN %s-%s" % (returned, move_line))
                     #     if move_line.product_id == returned.product_id and move_line.lot_id == returned.lot_id:
                     #         if move_line.qty_done == returned.qty_done:
                     #             del_move_lines |= move_line
                     #             continue
                     #         quantitys[move_line] = move_line.qty_done - returned.qty_done
                 # _logger.info("DEL LINES %s-%s=%s" % (del_move_lines, current_move_lines, current_move_lines - del_move_lines))
                 moves = current_move_lines - del_move_lines
                 for owner_id, own_lines in groupby(
                         moves.sorted(lambda r: r.owner_id, reverse=True),
                         lambda l: l.owner_id):
                     lines_own = []
                     for line in own_lines:
                         lines_own.append(line)
                     for product_set, lines in groupby(
                             sorted(lines_own,
                                    key=lambda r: r.product_set_id,
                                    reverse=True),
                             lambda l: l.product_set_id):
                         lines_lot = []
                         for line in lines:
                             lines_lot.append(line)
                         for lot_id, line_lot in groupby(
                                 sorted(lines_lot,
                                        key=lambda k: k.lot_id,
                                        reverse=True), lambda r: r.lot_id):
                             quantity = sum(
                                 [quantitys.get(r, 0.0) for r in line_lot])
                             quantity = float_round(quantity,
                                                    precision_rounding=move.
                                                    product_uom.rounding)
                             # _logger.info("QTY %s(%s)==>%s=%s" % (product_set, [r.qty_done for r in moves.filtered(lambda r: r.product_set_id == product_set)], quantity, quantity_save))
                             quantity_save -= quantity
                             product_return_moves.append((0, 0, {
                                 'product_id':
                                 move.product_id.id,
                                 'quantity':
                                 quantity,
                                 'move_id':
                                 move.id,
                                 'uom_id':
                                 move.product_id.uom_id.id,
                                 'lot_id':
                                 lot_id and lot_id.id or False,
                                 'owner_id':
                                 owner_id and owner_id.id or False,
                                 'product_set_id':
                                 product_set.id
                             }))
             if product_return_moves:
                 res['product_return_moves'] = product_return_moves
             else:
                 res['product_return_moves'] = False
     return res
Exemplo n.º 48
0
    def _free_reservation(self,
                          product_id,
                          location_id,
                          quantity,
                          lot_id=None,
                          package_id=None,
                          owner_id=None):
        """ When editing a done move line or validating one with some forced quantities, it is
        possible to impact quants that were not reserved. It is therefore necessary to edit or
        unlink the move lines that reserved a quantity now unavailable.
        """
        self.ensure_one()

        # Check the available quantity, with the `strict` kw set to `True`. If the available
        # quantity is greather than the quantity now unavailable, there is nothing to do.
        available_quantity = self.env['stock.quant']._get_available_quantity(
            product_id,
            location_id,
            lot_id=lot_id,
            package_id=package_id,
            owner_id=owner_id,
            strict=True)
        if quantity > available_quantity:
            # We now have to find the move lines that reserved our now unavailable quantity. We
            # take care to exclude ourselves and the move lines were work had already been done.
            oudated_move_lines_domain = [
                ('move_id.state', 'not in', ['done', 'cancel']),
                ('product_id', '=', product_id.id),
                ('lot_id', '=', lot_id.id if lot_id else False),
                ('location_id', '=', location_id.id),
                ('owner_id', '=', owner_id.id if owner_id else False),
                ('package_id', '=', package_id.id if package_id else False),
                ('product_qty', '>', 0.0),
                ('qty_done', '=', 0.0),
                ('id', '!=', self.id),
            ]
            oudated_candidates = self.env['stock.move.line'].search(
                oudated_move_lines_domain)

            # As the move's state is not computed over the move lines, we'll have to manually
            # recompute the moves which we adapted their lines.
            move_to_recompute_state = self.env['stock.move']

            rounding = self.product_uom_id.rounding
            for candidate in oudated_candidates:
                if float_compare(candidate.product_qty,
                                 quantity,
                                 precision_rounding=rounding) <= 0:
                    quantity -= candidate.product_qty
                    move_to_recompute_state |= candidate.move_id
                    candidate.unlink()
                else:
                    # split this move line and assign the new part to our extra move
                    quantity_split = float_round(
                        candidate.product_qty - quantity,
                        precision_rounding=self.product_uom_id.rounding,
                        rounding_method='UP')
                    candidate.product_uom_qty = self.product_id.uom_id._compute_quantity(
                        quantity_split,
                        self.product_uom_id,
                        rounding_method='HALF-UP')
                    quantity -= quantity_split
                    move_to_recompute_state |= candidate.move_id
                if quantity == 0.0:
                    break
            move_to_recompute_state._recompute_state()
 def _compute_total_allocation_used(self):
     for employee in self:
         employee.allocation_used_count = float_round(employee.allocation_count - employee.remaining_leaves, precision_digits=2)
         employee.allocation_used_display = "%g" % employee.allocation_used_count
Exemplo n.º 50
0
 def _compute_sales_count(self):
     for product in self:
         product.sales_count = float_round(sum([p.sales_count for p in product.with_context(active_test=False).product_variant_ids]), precision_rounding=product.uom_id.rounding)
Exemplo n.º 51
0
    def test_01_compute_price_operation_cost(self):
        """Test calcuation of bom cost with operations."""
        workcenter_from1 = Form(self.env['mrp.workcenter'])
        workcenter_from1.name = 'Workcenter'
        workcenter_from1.time_efficiency = 100
        workcenter_from1.capacity = 2
        workcenter_from1.oee_target = 100
        workcenter_from1.time_start = 0
        workcenter_from1.time_stop = 0
        workcenter_from1.costs_hour = 100
        workcenter_1 = workcenter_from1.save()

        routing_form1 = Form(self.Routing)
        routing_form1.name = 'Assembly Furniture'
        routing_1 = routing_form1.save()

        operation_1 = self.operation.create({
            'name': 'Cutting',
            'workcenter_id': workcenter_1.id,
            'routing_id': routing_1.id,
            'time_mode': 'manual',
            'time_cycle_manual': 20,
            'batch': 'no',
            'sequence': 1,
        })
        operation_2 = self.operation.create({
            'name': 'Drilling',
            'workcenter_id': workcenter_1.id,
            'routing_id': routing_1.id,
            'time_mode': 'manual',
            'time_cycle_manual': 25,
            'batch': 'no',
            'sequence': 2,
        })
        operation_3 = self.operation.create({
            'name': 'Fitting',
            'workcenter_id': workcenter_1.id,
            'routing_id': routing_1.id,
            'time_mode': 'manual',
            'time_cycle_manual': 30,
            'batch': 'no',
            'sequence': 3,
        })

        # -----------------------------------------------------------------
        # Dinning Table Operation Cost(1 Unit)
        # -----------------------------------------------------------------
        # Operation cost calculate for 1 units
        # Cutting        (20 / 60) * 100 =  33.33
        # Drilling       (25 / 60) * 100 =  41.67
        # Fitting        (30 / 60) * 100 =  50.00
        # ----------------------------------------
        # Operation Cost  1 unit = 125
        # -----------------------------------------------------------------

        self.bom_1.routing_id = routing_1.id

        # --------------------------------------------------------------------------
        # Table Head Operation Cost (1 Dozen)
        # --------------------------------------------------------------------------
        # Operation cost calculate for 1 dozens
        # Cutting        (20 * 1 / 60) * 100 =  33,33
        # Drilling       (25 * 1 / 60) * 100 =  41,67
        # Fitting        (30 * 1 / 60) * 100 =  50
        # ----------------------------------------
        # Operation Cost 1 dozen (125 per dozen) and 10.42 for 1 Unit
        # --------------------------------------------------------------------------

        self.bom_2.routing_id = routing_1.id

        self.assertEqual(self.dining_table.standard_price, 1000, "Initial price of the Product should be 1000")
        self.dining_table.button_bom_cost()
        # Total cost of Dining Table = (550) + Total cost of operations (125) = 675.0
        self.assertEquals(float_round(self.dining_table.standard_price, precision_digits=2), 675.0, "After computing price from BoM price should be 612.5")
        self.Product.browse([self.dining_table.id, self.table_head.id]).action_bom_cost()
        # Total cost of Dining Table = (718.75) + Total cost of all operations (125 + 10.42) = 854.17
        self.assertEquals(float_compare(self.dining_table.standard_price, 854.17, precision_digits=2), 0, "After computing price from BoM price should be 786.46")
Exemplo n.º 52
0
 def _compute_amount_total(self):
     super(AccountInvoiceTax, self)._compute_amount_total()
     currency = self.invoice_id.currency_id or \
         self.invoice_id.company_id.currency_id
     self.amount_deductible = float_round(
         self.amount_total * self.deduction_rate, currency.decimal_places)
Exemplo n.º 53
0
    def _action_done(self):
        """ This method is called during a move's `action_done`. It'll actually move a quant from
        the source location to the destination location, and unreserve if needed in the source
        location.

        This method is intended to be called on all the move lines of a move. This method is not
        intended to be called when editing a `done` move (that's what the override of `write` here
        is done.
        """

        # First, we loop over all the move lines to do a preliminary check: `qty_done` should not
        # be negative and, according to the presence of a picking type or a linked inventory
        # adjustment, enforce some rules on the `lot_id` field. If `qty_done` is null, we unlink
        # the line. It is mandatory in order to free the reservation and correctly apply
        # `action_done` on the next move lines.
        ml_to_delete = self.env['stock.move.line']
        for ml in self:
            # Check here if `ml.qty_done` respects the rounding of `ml.product_uom_id`.
            uom_qty = float_round(ml.qty_done, precision_rounding=ml.product_uom_id.rounding, rounding_method='HALF-UP')
            precision_digits = self.env['decimal.precision'].precision_get('Product Unit of Measure')
            qty_done = float_round(ml.qty_done, precision_digits=precision_digits, rounding_method='HALF-UP')
            if float_compare(uom_qty, qty_done, precision_digits=precision_digits) != 0:
                raise UserError(_('The quantity done for the product "%s" doesn\'t respect the rounding precision \
                                  defined on the unit of measure "%s". Please change the quantity done or the \
                                  rounding precision of your unit of measure.') % (ml.product_id.display_name, ml.product_uom_id.name))

            qty_done_float_compared = float_compare(ml.qty_done, 0, precision_rounding=ml.product_uom_id.rounding)
            if qty_done_float_compared > 0:
                if ml.product_id.tracking != 'none':
                    picking_type_id = ml.move_id.picking_type_id
                    if picking_type_id:
                        if picking_type_id.use_create_lots:
                            # If a picking type is linked, we may have to create a production lot on
                            # the fly before assigning it to the move line if the user checked both
                            # `use_create_lots` and `use_existing_lots`.
                            if ml.lot_name and not ml.lot_id:
                                lot = self.env['stock.production.lot'].create(
                                    {'name': ml.lot_name, 'product_id': ml.product_id.id}
                                )
                                ml.write({'lot_id': lot.id})
                        elif not picking_type_id.use_create_lots and not picking_type_id.use_existing_lots:
                            # If the user disabled both `use_create_lots` and `use_existing_lots`
                            # checkboxes on the picking type, he's allowed to enter tracked
                            # products without a `lot_id`.
                            continue
                    elif ml.move_id.inventory_id:
                        # If an inventory adjustment is linked, the user is allowed to enter
                        # tracked products without a `lot_id`.
                        continue

                    if not ml.lot_id:
                        raise UserError(_('You need to supply a Lot/Serial number for product %s.') % ml.product_id.display_name)
            elif qty_done_float_compared < 0:
                raise UserError(_('No negative quantities allowed'))
            else:
                ml_to_delete |= ml
        ml_to_delete.unlink()

        # Now, we can actually move the quant.
        done_ml = self.env['stock.move.line']
        for ml in self - ml_to_delete:
            if ml.product_id.type == 'product':
                Quant = self.env['stock.quant']
                rounding = ml.product_uom_id.rounding

                # if this move line is force assigned, unreserve elsewhere if needed
                if not ml.location_id.should_bypass_reservation() and float_compare(ml.qty_done, ml.product_qty, precision_rounding=rounding) > 0:
                    extra_qty = ml.qty_done - ml.product_qty
                    ml._free_reservation(ml.product_id, ml.location_id, extra_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id, ml_to_ignore=done_ml)
                # unreserve what's been reserved
                if not ml.location_id.should_bypass_reservation() and ml.product_id.type == 'product' and ml.product_qty:
                    try:
                        Quant._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id, strict=True)
                    except UserError:
                        Quant._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id, strict=True)

                # move what's been actually done
                quantity = ml.product_uom_id._compute_quantity(ml.qty_done, ml.move_id.product_id.uom_id, rounding_method='HALF-UP')
                available_qty, in_date = Quant._update_available_quantity(ml.product_id, ml.location_id, -quantity, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id)
                if available_qty < 0 and ml.lot_id:
                    # see if we can compensate the negative quants with some untracked quants
                    untracked_qty = Quant._get_available_quantity(ml.product_id, ml.location_id, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id, strict=True)
                    if untracked_qty:
                        taken_from_untracked_qty = min(untracked_qty, abs(quantity))
                        Quant._update_available_quantity(ml.product_id, ml.location_id, -taken_from_untracked_qty, lot_id=False, package_id=ml.package_id, owner_id=ml.owner_id)
                        Quant._update_available_quantity(ml.product_id, ml.location_id, taken_from_untracked_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id)
                Quant._update_available_quantity(ml.product_id, ml.location_dest_id, quantity, lot_id=ml.lot_id, package_id=ml.result_package_id, owner_id=ml.owner_id, in_date=in_date)
            done_ml |= ml
        # Reset the reserved quantity as we just moved it to the destination location.
        (self - ml_to_delete).with_context(bypass_reservation_update=True).write({
            'product_uom_qty': 0.00,
            'date': fields.Datetime.now(),
        })
    def get_write_data(self, line, col_dict):
        """ Returns value and style for cell """
        cell_type = col_dict.get('type', 'string')
        field = col_dict.get('field')
        decimals = self.currency.decimal_places

        value = getattr(line, field, False)
        style = None
        allow = False

        if cell_type == 'many2one':
            val_name = getattr(value, 'name', '')
            val_display = getattr(value, 'display_name', '')
            if val_name:
                value = val_name
            elif val_display:
                value = val_display
            elif line._name == 'account_balance_report_partner':
                value = _("No partner allocated")
        elif cell_type == 'string':
            if getattr(line, 'account_group_id', False):
                style = self.format_bold
            else:
                style = None
        elif cell_type == 'amount':
            value = float_round(float(value), decimals)
            if getattr(line, 'account_group_id', False):
                style = self.format_amount_bold_right
            else:
                style = self.format_amount_right
            allow = True
        elif cell_type == 'amount_currency':
            currency = getattr(line, 'currency_id', False) \
                or getattr(line, 'company_currency_id', False) \
                or self.currency
            decimals = currency.decimal_places
            value = float_round(float(value), decimals)
            if getattr(line, 'account_group_id', False):
                style = self.format_amount_bold_right
            else:
                style = self.format_amount_right
            allow = True

        if value:
            if isinstance(value, (int, float)) \
                    and cell_type not in ('amount', 'amount_currency'):
                value = format(value, '.{}f'.format(decimals))
            if not isinstance(value, str) \
                    and cell_type not in ('amount', 'amount_currency'):
                value = str(value)
            indent_field, indent_unit = self.get_indent_data(line, col_dict)
            if self.report.hierarchy_on != 'none' \
                    and indent_field and indent_unit \
                    and hasattr(line, indent_field):
                indent = ' ' * getattr(line, indent_field, 0) * indent_unit
                value = indent + value
            allow = True

        if allow and isinstance(value, float) \
                and float_is_zero(value, decimals):
            value = format(value, '.{}f'.format(decimals))

        return value, style, allow
Exemplo n.º 55
0
 def _compute_secondary_unit_qty_available(self):
     for product in self.filtered('stock_secondary_uom_id'):
         qty = product.qty_available / (
             product.stock_secondary_uom_id.factor or 1.0)
         product.secondary_unit_qty_available = float_round(
             qty, precision_rounding=product.uom_id.rounding)
Exemplo n.º 56
0
    def _free_reservation(self, product_id, location_id, quantity, lot_id=None, package_id=None, owner_id=None, ml_ids_to_ignore=None):
        """ When editing a done move line or validating one with some forced quantities, it is
        possible to impact quants that were not reserved. It is therefore necessary to edit or
        unlink the move lines that reserved a quantity now unavailable.

        :param ml_ids_to_ignore: OrderedSet of `stock.move.line` ids that should NOT be unreserved
        """
        self.ensure_one()

        if ml_ids_to_ignore is None:
            ml_ids_to_ignore = OrderedSet()
        ml_ids_to_ignore |= self.ids

        # Check the available quantity, with the `strict` kw set to `True`. If the available
        # quantity is greather than the quantity now unavailable, there is nothing to do.
        available_quantity = self.env['stock.quant']._get_available_quantity(
            product_id, location_id, lot_id=lot_id, package_id=package_id, owner_id=owner_id, strict=True
        )
        if quantity > available_quantity:
            # We now have to find the move lines that reserved our now unavailable quantity. We
            # take care to exclude ourselves and the move lines were work had already been done.
            outdated_move_lines_domain = [
                ('state', 'not in', ['done', 'cancel']),
                ('product_id', '=', product_id.id),
                ('lot_id', '=', lot_id.id if lot_id else False),
                ('location_id', '=', location_id.id),
                ('owner_id', '=', owner_id.id if owner_id else False),
                ('package_id', '=', package_id.id if package_id else False),
                ('product_qty', '>', 0.0),
                ('id', 'not in', tuple(ml_ids_to_ignore)),
            ]

            # We take the current picking first, then the pickings with the latest scheduled date
            current_picking_first = lambda cand: (
                cand.picking_id != self.move_id.picking_id,
                -(cand.picking_id.scheduled_date or cand.move_id.date).timestamp()
                if cand.picking_id or cand.move_id
                else -cand.id,
            )
            outdated_candidates = self.env['stock.move.line'].search(outdated_move_lines_domain).sorted(current_picking_first)

            # As the move's state is not computed over the move lines, we'll have to manually
            # recompute the moves which we adapted their lines.
            move_to_recompute_state = self.env['stock.move']
            to_unlink_candidate_ids = set()

            rounding = self.product_uom_id.rounding
            for candidate in outdated_candidates:
                if float_compare(candidate.product_qty, quantity, precision_rounding=rounding) <= 0:
                    quantity -= candidate.product_qty
                    if candidate.qty_done:
                        move_to_recompute_state |= candidate.move_id
                        candidate.product_uom_qty = 0.0
                    else:
                        to_unlink_candidate_ids.add(candidate.id)
                    if float_is_zero(quantity, precision_rounding=rounding):
                        break
                else:
                    # split this move line and assign the new part to our extra move
                    quantity_split = float_round(
                        candidate.product_qty - quantity,
                        precision_rounding=self.product_uom_id.rounding,
                        rounding_method='UP')
                    candidate.product_uom_qty = self.product_id.uom_id._compute_quantity(quantity_split, candidate.product_uom_id, rounding_method='HALF-UP')
                    move_to_recompute_state |= candidate.move_id
                    break
            self.env['stock.move.line'].browse(to_unlink_candidate_ids).unlink()
            move_to_recompute_state._recompute_state()
Exemplo n.º 57
0
 def _compute_purchased_product_qty(self):
     for template in self:
         template.purchased_product_qty = float_round(sum([p.purchased_product_qty for p in template.product_variant_ids]), precision_rounding=template.uom_id.rounding)
Exemplo n.º 58
0
    def _create_stripe_charge(self, acquirer_ref=None, tokenid=None, email=None):
        api_url_charge = 'https://%s/charges' % (self.acquirer_id._get_stripe_api_url())
        charge_params = {
            'amount': int(self.amount if self.currency_id.name in INT_CURRENCIES else float_round(self.amount * 100, 2)),
            'currency': self.currency_id.name,
            'metadata[reference]': self.reference,
            'description': self.reference,
        }
        if acquirer_ref:
            charge_params['customer'] = acquirer_ref
        if tokenid:
            charge_params['card'] = str(tokenid)
        if email:
            charge_params['receipt_email'] = email.strip()

        _logger.info('_create_stripe_charge: Sending values to URL %s, values:\n%s', api_url_charge, pprint.pformat(charge_params))
        r = requests.post(api_url_charge,
                          auth=(self.acquirer_id.stripe_secret_key, ''),
                          params=charge_params,
                          headers=STRIPE_HEADERS)
        res = r.json()
        _logger.info('_create_stripe_charge: Values received:\n%s', pprint.pformat(res))
        return res
Exemplo n.º 59
0
 def _get_margin_value(self, value, previous_value=0.0):
     margin = 0.0
     if (value != previous_value) and (value != 0.0 and previous_value != 0.0):
         margin = float_round((float(value-previous_value) / previous_value or 1) * 100, precision_digits=2)
     return margin
Exemplo n.º 60
0
 def _compute_remaining_leaves(self):
     remaining = self._get_remaining_leaves()
     for employee in self:
         employee.remaining_leaves = float_round(remaining.get(
             employee.id, 0.0),
                                                 precision_digits=2)