Beispiel #1
0
 def _compute_invoice_status(self):
     """
     Compute the invoice status of a SO line. Possible statuses:
     - no: if the SO is not in status 'sale' or 'done', we consider that there is nothing to
       invoice. This is also hte default value if the conditions of no other status is met.
     - to invoice: we refer to the quantity to invoice of the line. Refer to method
       `_get_to_invoice_qty()` for more information on how this quantity is calculated.
     - upselling: this is possible only for a product invoiced on ordered quantities for which
       we delivered more than expected. The could arise if, for example, a project took more
       time than expected but we decided not to invoice the extra cost to the client. This
       occurs onyl in state 'sale', so that when a SO is set to done, the upselling opportunity
       is removed from the list.
     - invoiced: the quantity invoiced is larger or equal to the quantity ordered.
     """
     precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
     for line in self:
         if line.state not in ('sale', 'done'):
             line.invoice_status = 'no'
         elif not float_is_zero(line.qty_to_invoice, precision_digits=precision):
             line.invoice_status = 'to invoice'
         elif line.state == 'sale' and line.product_id.invoice_policy == 'order' and\
                 float_compare(line.qty_delivered, line.product_uom_qty, precision_digits=precision) == 1:
             line.invoice_status = 'upselling'
         elif float_compare(line.qty_invoiced, line.product_uom_qty, precision_digits=precision) >= 0:
             line.invoice_status = 'invoiced'
         else:
             line.invoice_status = 'no'
Beispiel #2
0
    def _check_sum(self, cr, uid, landed_cost, context=None):
        """
        Will check if each cost line its valuation lines sum to the correct amount
        and if the overall total amount is correct also
        """
        costcor = {}
        tot = 0
        for valuation_line in landed_cost.valuation_adjustment_lines:
            if costcor.get(valuation_line.cost_line_id):
                costcor[valuation_line.
                        cost_line_id] += valuation_line.additional_landed_cost
            else:
                costcor[valuation_line.
                        cost_line_id] = valuation_line.additional_landed_cost
            tot += valuation_line.additional_landed_cost

        prec = self.pool['decimal.precision'].precision_get(cr, uid, 'Account')
        # float_compare returns 0 for equal amounts
        res = not bool(
            float_compare(tot, landed_cost.amount_total,
                          precision_digits=prec))
        for costl in costcor.keys():
            if float_compare(costcor[costl],
                             costl.price_unit,
                             precision_digits=prec):
                res = False
        return res
Beispiel #3
0
    def do_move_consume(self, cr, uid, ids, context=None):
        if context is None:
            context = {}
        move_obj = self.pool.get('stock.move')
        uom_obj = self.pool.get('product.uom')
        production_obj = self.pool.get('mrp.production')
        move_ids = context['active_ids']
        move = move_obj.browse(cr, uid, move_ids[0], context=context)
        production_id = move.raw_material_production_id.id
        production = production_obj.browse(cr, uid, production_id, context=context)
        precision = self.pool['decimal.precision'].precision_get(cr, uid, 'Product Unit of Measure')

        for data in self.browse(cr, uid, ids, context=context):
            qty = uom_obj._compute_qty(cr, uid, data['product_uom'].id, data.product_qty, data.product_id.uom_id.id)
            remaining_qty = move.product_qty - qty
            #check for product quantity is less than previously planned
            if float_compare(remaining_qty, 0, precision_digits=precision) >= 0:
                move_obj.action_consume(cr, uid, move_ids, qty, data.location_id.id, restrict_lot_id=data.restrict_lot_id.id, context=context)
            else:
                consumed_qty = min(move.product_qty, qty)
                new_moves = move_obj.action_consume(cr, uid, move_ids, consumed_qty, data.location_id.id, restrict_lot_id=data.restrict_lot_id.id, context=context)
                #consumed more in wizard than previously planned
                extra_more_qty = qty - consumed_qty
                #create new line for a remaining qty of the product
                extra_move_id = production_obj._make_consume_line_from_data(cr, uid, production, data.product_id, data.product_id.uom_id.id, extra_more_qty, context=context)
                move_obj.write(cr, uid, [extra_move_id], {'restrict_lot_id': data.restrict_lot_id.id}, context=context)
                move_obj.action_done(cr, uid, [extra_move_id], context=context)
        return {'type': 'ir.actions.act_window_close'}
Beispiel #4
0
    def _action_procurement_create(self):
        """
        Create procurements based on quantity ordered. If the quantity is increased, new
        procurements are created. If the quantity is decreased, no automated action is taken.
        """
        precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
        new_procs = self.env['procurement.order'] #Empty recordset
        for line in self:
            if line.state != 'sale' or not line.product_id._need_procurement():
                continue
            qty = 0.0
            for proc in line.procurement_ids:
                qty += proc.product_qty
            if float_compare(qty, line.product_uom_qty, precision_digits=precision) >= 0:
                return False

            if not line.order_id.procurement_group_id:
                vals = line.order_id._prepare_procurement_group()
                line.order_id.procurement_group_id = self.env["procurement.group"].create(vals)

            vals = line._prepare_order_line_procurement(group_id=line.order_id.procurement_group_id.id)
            vals['product_qty'] = line.product_uom_qty - qty
            new_proc = self.env["procurement.order"].create(vals)
            new_procs += new_proc
        new_procs.run()
        return new_procs
Beispiel #5
0
    def _get_delivered_qty(self):
        self.ensure_one()
        precision = self.env['decimal.precision'].precision_get(
            'Product Unit of Measure')

        # In the case of a kit, we need to check if all components are shipped. We use a all or
        # nothing policy. A product can have several BoMs, we don't know which one was used when the
        # delivery was created.
        bom_delivered = {}
        for bom in self.product_id.product_tmpl_id.bom_ids:
            if bom.type != 'phantom':
                continue
            bom_delivered[bom.id] = False
            bom_exploded = self.env['mrp.bom']._bom_explode(
                bom, self.product_id, self.product_uom_qty)[0]
            for bom_line in bom_exploded:
                qty = 0.0
                for move in self.procurement_ids.mapped('move_ids'):
                    if move.state == 'done' and move.product_id.id == bom_line.get(
                            'product_id', False):
                        qty += self.env['product.uom']._compute_qty_obj(
                            move.product_uom, move.product_uom_qty,
                            self.product_uom)
                if float_compare(qty,
                                 bom_line['product_qty'],
                                 precision_digits=precision) < 0:
                    bom_delivered[bom.id] = False
                    break
                else:
                    bom_delivered[bom.id] = True
        if bom_delivered and any(bom_delivered.values()):
            return self.product_uom_qty
        elif bom_delivered:
            return 0.0
        return super(SaleOrderLine, self)._get_delivered_qty()
Beispiel #6
0
 def write(self, values):
     lines = False
     if 'product_uom_qty' in values:
         precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
         lines = self.filtered(
             lambda r: r.state == 'sale' and float_compare(r.product_uom_qty, values['product_uom_qty'], precision_digits=precision) == -1)
     result = super(SaleOrderLine, self).write(values)
     if lines:
         lines._action_procurement_create()
     return result
Beispiel #7
0
 def _onchange_product_id_check_availability(self):
     if not self.product_id or not self.product_uom_qty or not self.product_uom:
         self.product_packaging = False
         return {}
     if self.product_id.type == 'product':
         precision = self.env['decimal.precision'].precision_get(
             'Product Unit of Measure')
         product_qty = self.env['product.uom']._compute_qty_obj(
             self.product_uom, self.product_uom_qty, self.product_id.uom_id)
         if float_compare(self.product_id.virtual_available,
                          product_qty,
                          precision_digits=precision) == -1:
             is_available = self._check_routing()
             if not is_available:
                 warning_mess = {
                     'title': _('Not enough inventory!'),
                     'message' : _('You plan to sell %.2f %s but you only have %.2f %s available!\nThe stock on hand is %.2f %s.') % \
                         (self.product_uom_qty, self.product_uom.name, self.product_id.virtual_available, self.product_id.uom_id.name, self.product_id.qty_available, self.product_id.uom_id.name)
                 }
                 return {'warning': warning_mess}
     return {}
Beispiel #8
0
    def process_sheet(self, cr, uid, ids, context=None):
        move_pool = self.pool.get('account.move')
        hr_payslip_line_pool = self.pool['hr.payslip.line']
        precision = self.pool.get('decimal.precision').precision_get(
            cr, uid, 'Payroll')
        timenow = time.strftime('%Y-%m-%d')

        for slip in self.browse(cr, uid, ids, context=context):
            line_ids = []
            debit_sum = 0.0
            credit_sum = 0.0
            date = timenow

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

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

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

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

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

            move.update({'line_ids': line_ids})
            move_id = move_pool.create(cr, uid, move, context=context)
            self.write(cr,
                       uid, [slip.id], {
                           'move_id': move_id,
                           'date': date
                       },
                       context=context)
            move_pool.post(cr, uid, [move_id], context=context)
        return super(hr_payslip, self).process_sheet(cr,
                                                     uid, [slip.id],
                                                     context=context)
Beispiel #9
0
    def _procure_orderpoint_confirm(self,
                                    cr,
                                    uid,
                                    use_new_cursor=False,
                                    company_id=False,
                                    context=None):
        '''
        Create procurement based on Orderpoint

        :param bool use_new_cursor: if set, use a dedicated cursor and auto-commit after processing each procurement.
            This is appropriate for batch jobs only.
        '''
        if context is None:
            context = {}
        if use_new_cursor:
            cr = yuancloud.registry(cr.dbname).cursor()
        orderpoint_obj = self.pool.get('stock.warehouse.orderpoint')
        procurement_obj = self.pool.get('procurement.order')
        product_obj = self.pool.get('product.product')
        dom = company_id and [('company_id', '=', company_id)] or []
        orderpoint_ids = orderpoint_obj.search(
            cr,
            uid,
            dom,
            order="location_id, purchase_calendar_id, calendar_id")
        prev_ids = []
        tot_procs = []
        while orderpoint_ids:
            ids = orderpoint_ids[:1000]
            del orderpoint_ids[:1000]
            dates_dict = {}
            product_dict = {}
            ops_dict = {}
            ops = orderpoint_obj.browse(cr, uid, ids, context=context)

            #Calculate groups that can be executed together
            for op in ops:
                key = (op.location_id.id, op.purchase_calendar_id.id,
                       op.calendar_id.id)
                res_groups = []
                if not dates_dict.get(key):
                    date_groups = self._get_group(cr, uid, op, context=context)
                    for date, group in date_groups:
                        if op.calendar_id and op.calendar_id.attendance_ids:
                            date1, date2 = self._get_next_dates(
                                cr, uid, op, date, group, context=context)
                            res_groups += [
                                (group, date1, date2, date)
                            ]  #date1/date2 as deliveries and date as purchase confirmation date
                        else:
                            res_groups += [(group, date, False, date)]
                    dates_dict[key] = res_groups
                    product_dict[key] = [op.product_id]
                    ops_dict[key] = [op]
                else:
                    product_dict[key] += [op.product_id]
                    ops_dict[key] += [op]

            for key in product_dict.keys():
                for res_group in dates_dict[key]:
                    ctx = context.copy()
                    ctx.update({'location': ops_dict[key][0].location_id.id})
                    if res_group[2]:
                        ctx.update({
                            'to_date':
                            res_group[2].strftime(
                                DEFAULT_SERVER_DATETIME_FORMAT)
                        })
                    prod_qty = product_obj._product_available(
                        cr,
                        uid, [x.id for x in product_dict[key]],
                        context=ctx)
                    group = res_group[0]
                    date = res_group[1]
                    subtract_qty = orderpoint_obj.subtract_procurements_from_orderpoints(
                        cr,
                        uid, [x.id for x in ops_dict[key]],
                        context=context)
                    first_op = True
                    ndelivery = date and self._convert_to_UTC(
                        cr, uid, date, context=context) or False
                    npurchase = res_group[3] and self._convert_to_UTC(
                        cr, uid, res_group[3], context=context) or False
                    for op in ops_dict[key]:
                        try:
                            prods = prod_qty[
                                op.product_id.id]['virtual_available']
                            if prods is None:
                                continue

                            if float_compare(prods,
                                             op.product_min_qty,
                                             precision_rounding=op.product_uom.
                                             rounding) < 0:
                                qty = max(op.product_min_qty,
                                          op.product_max_qty) - prods
                                reste = op.qty_multiple > 0 and qty % op.qty_multiple or 0.0
                                if float_compare(reste,
                                                 0.0,
                                                 precision_rounding=op.
                                                 product_uom.rounding) > 0:
                                    qty += op.qty_multiple - reste

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

                                qty -= subtract_qty[op.id]

                                qty_rounded = float_round(
                                    qty,
                                    precision_rounding=op.product_uom.rounding)
                                if qty_rounded > 0:
                                    proc_id = procurement_obj.create(
                                        cr,
                                        uid,
                                        self._prepare_orderpoint_procurement(
                                            cr,
                                            uid,
                                            op,
                                            qty_rounded,
                                            date=ndelivery,
                                            purchase_date=npurchase,
                                            group=group,
                                            context=context),
                                        context=context)
                                    tot_procs.append(proc_id)
                                    orderpoint_obj.write(
                                        cr,
                                        uid, [op.id], {
                                            'last_execution_date':
                                            datetime.utcnow().strftime(
                                                DEFAULT_SERVER_DATETIME_FORMAT)
                                        },
                                        context=context)
                                if use_new_cursor:
                                    cr.commit()
                        except OperationalError:
                            if use_new_cursor:
                                orderpoint_ids.append(op.id)
                                cr.rollback()
                                continue
                            else:
                                raise
            try:
                tot_procs.reverse()
                self.run(cr, uid, tot_procs, context=context)
                tot_procs = []
                if use_new_cursor:
                    cr.commit()
            except OperationalError:
                if use_new_cursor:
                    cr.rollback()
                    continue
                else:
                    raise

            if use_new_cursor:
                cr.commit()
            if prev_ids == ids:
                break
            else:
                prev_ids = ids

        if use_new_cursor:
            cr.commit()
            cr.close()
        return {}
Beispiel #10
0
    def action_consume(self,
                       cr,
                       uid,
                       ids,
                       product_qty,
                       location_id=False,
                       restrict_lot_id=False,
                       restrict_partner_id=False,
                       consumed_for=False,
                       context=None):
        """ Consumed product with specific quantity from specific source location.
        @param product_qty: Consumed/produced product quantity (= in quantity of UoM of product)
        @param location_id: Source location
        @param restrict_lot_id: optionnal parameter that allows to restrict the choice of quants on this specific lot
        @param restrict_partner_id: optionnal parameter that allows to restrict the choice of quants to this specific partner
        @param consumed_for: optionnal parameter given to this function to make the link between raw material consumed and produced product, for a better traceability
        @return: New lines created if not everything was consumed for this line
        """
        if context is None:
            context = {}
        res = []
        production_obj = self.pool.get('mrp.production')

        if product_qty <= 0:
            raise UserError(_('Please provide proper quantity.'))
        #because of the action_confirm that can create extra moves in case of phantom bom, we need to make 2 loops
        ids2 = []
        for move in self.browse(cr, uid, ids, context=context):
            if move.state == 'draft':
                ids2.extend(
                    self.action_confirm(cr, uid, [move.id], context=context))
            else:
                ids2.append(move.id)

        prod_orders = set()
        for move in self.browse(cr, uid, ids2, context=context):
            prod_orders.add(move.raw_material_production_id.id
                            or move.production_id.id)
            move_qty = move.product_qty
            if move_qty <= 0:
                raise UserError(
                    _('Cannot consume a move with negative or zero quantity.'))
            quantity_rest = move_qty - product_qty
            # Compare with numbers of move uom as we want to avoid a split with 0 qty
            quantity_rest_uom = move.product_uom_qty - self.pool.get(
                "product.uom")._compute_qty_obj(
                    cr, uid, move.product_id.uom_id, product_qty,
                    move.product_uom)
            if float_compare(
                    quantity_rest_uom,
                    0,
                    precision_rounding=move.product_uom.rounding) != 0:
                new_mov = self.split(cr,
                                     uid,
                                     move,
                                     quantity_rest,
                                     context=context)
                if move.production_id:
                    self.write(cr,
                               uid, [new_mov],
                               {'production_id': move.production_id.id},
                               context=context)
                res.append(new_mov)
            vals = {
                'restrict_lot_id': restrict_lot_id,
                'restrict_partner_id': restrict_partner_id,
                'consumed_for': consumed_for
            }
            if location_id:
                vals.update({'location_id': location_id})
            self.write(cr, uid, [move.id], vals, context=context)
        # Original moves will be the quantities consumed, so they need to be done
        self.action_done(cr, uid, ids2, context=context)
        if res:
            self.action_assign(cr, uid, res, context=context)
        if prod_orders:
            production_obj.signal_workflow(cr, uid, list(prod_orders),
                                           'button_produce')
        return res
Beispiel #11
0
    def _procure_orderpoint_confirm(self, cr, uid, use_new_cursor=False, company_id=False, context=None):
        '''
        Create procurement based on Orderpoint

        :param bool use_new_cursor: if set, use a dedicated cursor and auto-commit after processing each procurement.
            This is appropriate for batch jobs only.
        '''
        if context is None:
            context = {}
        if use_new_cursor:
            cr = yuancloud.registry(cr.dbname).cursor()
        orderpoint_obj = self.pool.get('stock.warehouse.orderpoint')
        procurement_obj = self.pool.get('procurement.order')
        product_obj = self.pool.get('product.product')

        dom = company_id and [('company_id', '=', company_id)] or []
        orderpoint_ids = orderpoint_obj.search(cr, uid, dom, order="location_id")
        prev_ids = []
        tot_procs = []
        while orderpoint_ids:
            ids = orderpoint_ids[:1000]
            del orderpoint_ids[:1000]
            product_dict = {}
            ops_dict = {}
            ops = orderpoint_obj.browse(cr, uid, ids, context=context)

            #Calculate groups that can be executed together
            for op in ops:
                key = (op.location_id.id,)
                if not product_dict.get(key):
                    product_dict[key] = [op.product_id]
                    ops_dict[key] = [op]
                else:
                    product_dict[key] += [op.product_id]
                    ops_dict[key] += [op]

            for key in product_dict.keys():
                ctx = context.copy()
                ctx.update({'location': ops_dict[key][0].location_id.id})
                prod_qty = product_obj._product_available(cr, uid, [x.id for x in product_dict[key]],
                                                          context=ctx)
                subtract_qty = orderpoint_obj.subtract_procurements_from_orderpoints(cr, uid, [x.id for x in ops_dict[key]], context=context)
                for op in ops_dict[key]:
                    try:
                        prods = prod_qty[op.product_id.id]['virtual_available']
                        if prods is None:
                            continue
                        if float_compare(prods, op.product_min_qty, precision_rounding=op.product_uom.rounding) <= 0:
                            qty = max(op.product_min_qty, op.product_max_qty) - prods
                            reste = op.qty_multiple > 0 and qty % op.qty_multiple or 0.0
                            if float_compare(reste, 0.0, precision_rounding=op.product_uom.rounding) > 0:
                                qty += op.qty_multiple - reste

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

                            qty -= subtract_qty[op.id]

                            qty_rounded = float_round(qty, precision_rounding=op.product_uom.rounding)
                            if qty_rounded > 0:
                                proc_id = procurement_obj.create(cr, uid,
                                                                 self._prepare_orderpoint_procurement(cr, uid, op, qty_rounded, context=context),
                                                                 context=context)
                                tot_procs.append(proc_id)
                            if use_new_cursor:
                                cr.commit()
                    except OperationalError:
                        if use_new_cursor:
                            orderpoint_ids.append(op.id)
                            cr.rollback()
                            continue
                        else:
                            raise
            try:
                tot_procs.reverse()
                self.run(cr, uid, tot_procs, context=context)
                tot_procs = []
                if use_new_cursor:
                    cr.commit()
            except OperationalError:
                if use_new_cursor:
                    cr.rollback()
                    continue
                else:
                    raise

            if use_new_cursor:
                cr.commit()
            if prev_ids == ids:
                break
            else:
                prev_ids = ids

        if use_new_cursor:
            cr.commit()
            cr.close()
        return {}