Esempio n. 1
0
class PriceRule(models.Model):
    _name = "delivery.price.rule"
    _description = "Delivery Price Rules"
    _order = 'sequence, list_price'

    @api.depends('variable', 'operator', 'max_value', 'list_base_price', 'list_price', 'variable_factor')
    def _get_name(self):
        for rule in self:
            name = 'if %s %s %s then' % (rule.variable, rule.operator, rule.max_value)
            if rule.list_base_price and not rule.list_price:
                name = '%s fixed price %s' % (name, rule.list_base_price)
            elif rule.list_price and not rule.list_base_price:
                name = '%s %s times %s' % (name, rule.list_price, rule.variable_factor)
            else:
                name = '%s fixed price %s and %s times %s Extra' % (name, rule.list_base_price, rule.list_price, rule.variable_factor)
            rule.name = name

    name = fields.Char(compute='_get_name')
    sequence = fields.Integer(required=True, help="Gives the sequence order when calculating delivery carrier.", default=10)
    carrier_id = fields.Many2one('delivery.carrier', 'Carrier', required=True, ondelete='cascade')
    variable = fields.Selection([('weight', 'Weight'), ('volume', 'Volume'), ('wv', 'Weight * Volume'), ('price', 'Price'), ('quantity', 'Quantity')], 'Variable', required=True, default='weight')
    operator = fields.Selection([('==', '='), ('<=', '<='), ('<', '<'), ('>=', '>='), ('>', '>')], 'Operator', required=True, default='<=')
    max_value = fields.Float('Maximum Value', required=True)
    variable_factor = fields.Selection([('weight', 'Weight'), ('volume', 'Volume'), ('wv', 'Weight * Volume'), ('price', 'Price'), ('quantity', 'Quantity')], 'Variable Factor', required=True, default='weight')
    list_base_price = fields.Float(string='Sale Base Price', digits=dp.get_precision('Product Price'), required=True, default=0.0)
    list_price = fields.Float('Sale Price', digits=dp.get_precision('Product Price'), required=True, default=0.0)
    standard_price = fields.Float('Cost Price', digits=dp.get_precision('Product Price'), required=True, default=0.0)
Esempio n. 2
0
class hr_contract_be(osv.osv):
    _inherit = 'hr.contract'

    _columns = {
        'travel_reimbursement_amount':
        fields.float('Reimbursement of travel expenses',
                     digits_compute=dp.get_precision('Payroll')),
        'car_company_amount':
        fields.float('Company car employer',
                     digits_compute=dp.get_precision('Payroll')),
        'car_employee_deduction':
        fields.float('Company Car Deduction for Worker',
                     digits_compute=dp.get_precision('Payroll')),
        'misc_onss_deduction':
        fields.float('Miscellaneous exempt ONSS ',
                     digits_compute=dp.get_precision('Payroll')),
        'meal_voucher_amount':
        fields.float('Check Value Meal ',
                     digits_compute=dp.get_precision('Payroll')),
        'meal_voucher_employee_deduction':
        fields.float('Check Value Meal - by worker ',
                     digits_compute=dp.get_precision('Payroll')),
        'insurance_employee_deduction':
        fields.float('Insurance Group - by worker ',
                     digits_compute=dp.get_precision('Payroll')),
        'misc_advantage_amount':
        fields.float('Benefits of various nature ',
                     digits_compute=dp.get_precision('Payroll')),
        'additional_net_amount':
        fields.float('Net supplements',
                     digits_compute=dp.get_precision('Payroll')),
        'retained_net_amount':
        fields.float('Net retained ',
                     digits_compute=dp.get_precision('Payroll')),
    }
Esempio n. 3
0
class purchase_requisition_line(osv.osv):
    _name = "purchase.requisition.line"
    _description = "Purchase Requisition Line"
    _rec_name = 'product_id'

    _columns = {
        'product_id': fields.many2one('product.product', 'Product', domain=[('purchase_ok', '=', True)]),
        'product_uom_id': fields.many2one('product.uom', 'Product Unit of Measure'),
        'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure')),
        'requisition_id': fields.many2one('purchase.requisition', 'Call for Tenders', ondelete='cascade'),
        'company_id': fields.related('requisition_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True),
        'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account',),
        'schedule_date': fields.date('Scheduled Date'),
    }

    def onchange_product_id(self, cr, uid, ids, product_id, product_uom_id, parent_analytic_account, analytic_account, parent_date, date, context=None):
        """ Changes UoM and name if product_id changes.
        @param name: Name of the field
        @param product_id: Changed product_id
        @return:  Dictionary of changed values
        """
        value = {'product_uom_id': ''}
        if product_id:
            prod = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
            value = {'product_uom_id': prod.uom_id.id, 'product_qty': 1.0}
        if not analytic_account:
            value.update({'account_analytic_id': parent_analytic_account})
        if not date:
            value.update({'schedule_date': parent_date})
        return {'value': value}

    _defaults = {
        'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'purchase.requisition.line', context=c),
    }
Esempio n. 4
0
class res_company(osv.osv):
    _inherit = 'res.company'

    _columns = {
        'plafond_secu':
        fields.float('Plafond de la Securite Sociale',
                     digits_compute=dp.get_precision('Payroll')),
        'nombre_employes':
        fields.integer('Nombre d\'employes'),
        'cotisation_prevoyance':
        fields.float('Cotisation Patronale Prevoyance',
                     digits_compute=dp.get_precision('Payroll')),
        'org_ss':
        fields.char('Organisme de securite sociale'),
        'conv_coll':
        fields.char('Convention collective'),
    }
Esempio n. 5
0
class sale_order_line(osv.osv):
    _inherit = "sale.order.line"

    @api.multi
    @api.onchange('product_id', 'product_uom')
    def product_id_change_margin(self):
        for line in self:
            if line.order_id.pricelist_id:
                frm_cur = self.env.user.company_id.currency_id
                to_cur = line.order_id.pricelist_id.currency_id
                purchase_price = line.product_id.standard_price
                if line.product_uom != line.product_id.uom_id:
                    purchase_price = self.env['product.uom']._compute_price(
                        line.product_id.uom_id.id,
                        purchase_price,
                        to_uom_id=line.product_uom.id)
                ctx = self.env.context.copy()
                ctx['date'] = line.order_id.date_order
                price = frm_cur.with_context(ctx).compute(purchase_price,
                                                          to_cur,
                                                          round=False)
                line.purchase_price = price

    def _product_margin(self, cr, uid, ids, field_name, arg, context=None):
        cur_obj = self.pool.get('res.currency')
        res = {}
        for line in self.browse(cr, uid, ids, context=context):
            cur = line.order_id.pricelist_id.currency_id
            res[line.id] = 0
            if line.product_id:
                tmp_margin = line.price_subtotal - (
                    (line.purchase_price or line.product_id.standard_price) *
                    line.product_uom_qty)
                res[line.id] = cur_obj.round(cr, uid, cur, tmp_margin)
        return res

    _columns = {
        'margin':
        fields.function(_product_margin,
                        string='Margin',
                        digits_compute=dp.get_precision('Product Price'),
                        store=True),
        'purchase_price':
        fields.float('Cost', digits_compute=dp.get_precision('Product Price'))
    }
Esempio n. 6
0
class stock_valuation_adjustment_lines(osv.osv):
    _name = 'stock.valuation.adjustment.lines'
    _description = 'Stock Valuation Adjustment Lines'

    def _amount_final(self, cr, uid, ids, name, args, context=None):
        result = {}
        for line in self.browse(cr, uid, ids, context=context):
            result[line.id] = {
                'former_cost_per_unit': 0.0,
                'final_cost': 0.0,
            }
            result[line.id]['former_cost_per_unit'] = (line.former_cost / line.quantity if line.quantity else 1.0)
            result[line.id]['final_cost'] = (line.former_cost + line.additional_landed_cost)
        return result

    def _get_name(self, cr, uid, ids, name, arg, context=None):
        res = {}
        for line in self.browse(cr, uid, ids, context=context):
            res[line.id] = line.product_id.code or line.product_id.name or ''
            if line.cost_line_id:
                res[line.id] += ' - ' + line.cost_line_id.name
        return res

    _columns = {
        'name': fields.function(_get_name, type='char', string='Description', store=True),
        'cost_id': fields.many2one('stock.landed.cost', 'Landed Cost', required=True, ondelete='cascade'),
        'cost_line_id': fields.many2one('stock.landed.cost.lines', 'Cost Line', readonly=True),
        'move_id': fields.many2one('stock.move', 'Stock Move', readonly=True),
        'product_id': fields.many2one('product.product', 'Product', required=True),
        'quantity': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
        'weight': fields.float('Weight', digits_compute=dp.get_precision('Product Unit of Measure')),
        'volume': fields.float('Volume', digits_compute=dp.get_precision('Product Unit of Measure')),
        'former_cost': fields.float('Former Cost', digits_compute=dp.get_precision('Product Price')),
        'former_cost_per_unit': fields.function(_amount_final, multi='cost', string='Former Cost(Per Unit)', type='float', store=True, digits=0),
        'additional_landed_cost': fields.float('Additional Landed Cost', digits_compute=dp.get_precision('Product Price')),
        'final_cost': fields.function(_amount_final, multi='cost', string='Final Cost', type='float', store=True, digits=0),
    }

    _defaults = {
        'quantity': 1.0,
        'weight': 1.0,
        'volume': 1.0,
    }
Esempio n. 7
0
class hr_contract(osv.osv):
    """
    Employee contract allows to add different values in fields.
    Fields are used in salary rule computation.
    """

    _inherit = 'hr.contract'
    _description = 'HR Contract'

    _columns = {
        'tds':
        fields.float('TDS',
                     digits_compute=dp.get_precision('Payroll'),
                     help="Amount for Tax Deduction at Source"),
        'driver_salay':
        fields.boolean(
            'Driver Salary',
            help="Check this box if you provide allowance for driver"),
        'medical_insurance':
        fields.float(
            'Medical Insurance',
            digits_compute=dp.get_precision('Payroll'),
            help="Deduction towards company provided medical insurance"),
        'voluntary_provident_fund':
        fields.float(
            'Voluntary Provident Fund (%)',
            digits_compute=dp.get_precision('Payroll'),
            help=
            "VPF is a safe option wherein you can contribute more than the PF ceiling of 12% that has been mandated by the government and VPF computed as percentage(%)"
        ),
        'house_rent_allowance_metro_nonmetro':
        fields.float(
            'House Rent Allowance (%)',
            digits_compute=dp.get_precision('Payroll'),
            help=
            "HRA is an allowance given by the employer to the employee for taking care of his rental or accommodation expenses for metro city it is 50% and for non metro 40%. \nHRA computed as percentage(%)"
        ),
        'supplementary_allowance':
        fields.float('Supplementary Allowance',
                     digits_compute=dp.get_precision('Payroll')),
    }
Esempio n. 8
0
class LunchProduct(models.Model):
    """ Products available to order. A product is linked to a specific vendor. """
    _name = 'lunch.product'
    _description = 'lunch product'

    name = fields.Char('Product', required=True)
    category_id = fields.Many2one('lunch.product.category',
                                  'Category',
                                  required=True)
    description = fields.Text('Description')
    price = fields.Float('Price', digits=dp.get_precision('Account'))
    supplier = fields.Many2one('res.partner', 'Vendor')
Esempio n. 9
0
class membership_invoice(osv.osv_memory):
    """Membership Invoice"""

    _name = "membership.invoice"
    _description = "Membership Invoice"
    _columns = {
        'product_id': fields.many2one('product.product','Membership', required=True),
        'member_price': fields.float('Member Price', digits_compute= dp.get_precision('Product Price'), required=True),
    }
    def onchange_product(self, cr, uid, ids, product_id=False):
        """This function returns value of  product's member price based on product id.
        """
        if not product_id:
            return {'value': {'member_price': False}}
        return {'value': {'member_price': self.pool.get('product.product').price_get(cr, uid, [product_id])[product_id]}}

    def membership_invoice(self, cr, uid, ids, context=None):
        mod_obj = self.pool.get('ir.model.data')
        partner_obj = self.pool.get('res.partner')
        datas = {}
        if context is None:
            context = {}
        data = self.browse(cr, uid, ids, context=context)
        if data:
            data = data[0]
            datas = {
                'membership_product_id': data.product_id.id,
                'amount': data.member_price
            }
        invoice_list = partner_obj.create_membership_invoice(cr, uid, context.get('active_ids', []), datas=datas, context=context)

        try:
            search_view_id = mod_obj.get_object_reference(cr, uid, 'account', 'view_account_invoice_filter')[1]
        except ValueError:
            search_view_id = False
        try:
            form_view_id = mod_obj.get_object_reference(cr, uid, 'account', 'invoice_form')[1]
        except ValueError:
            form_view_id = False

        return  {
            'domain': [('id', 'in', invoice_list)],
            'name': 'Membership Invoices',
            'view_type': 'form',
            'view_mode': 'tree,form',
            'res_model': 'account.invoice',
            'type': 'ir.actions.act_window',
            'views': [(False, 'tree'), (form_view_id, 'form')],
            'search_view_id': search_view_id,
        }
Esempio n. 10
0
class payroll_advice_line(osv.osv):
    '''
    Bank Advice Lines
    '''
    def onchange_employee_id(self,
                             cr,
                             uid,
                             ids,
                             employee_id=False,
                             context=None):
        res = {}
        hr_obj = self.pool.get('hr.employee')
        if not employee_id:
            return {'value': res}
        employee = hr_obj.browse(cr, uid, [employee_id], context=context)[0]
        res.update({
            'name': employee.bank_account_id.acc_number,
            'ifsc_code': employee.bank_account_id.bank_bic or ''
        })
        return {'value': res}

    _name = 'hr.payroll.advice.line'
    _description = 'Bank Advice Lines'
    _columns = {
        'advice_id':
        fields.many2one('hr.payroll.advice', 'Bank Advice'),
        'name':
        fields.char('Bank Account No.', size=25, required=True),
        'ifsc_code':
        fields.char('IFSC Code', size=16),
        'employee_id':
        fields.many2one('hr.employee', 'Employee', required=True),
        'bysal':
        fields.float('By Salary', digits_compute=dp.get_precision('Payroll')),
        'debit_credit':
        fields.char('C/D', size=3, required=False),
        'company_id':
        fields.related('advice_id',
                       'company_id',
                       type='many2one',
                       required=False,
                       relation='res.company',
                       string='Company',
                       store=True),
        'ifsc':
        fields.related('advice_id', 'neft', type='boolean', string='IFSC'),
    }
    _defaults = {
        'debit_credit': 'C',
    }
Esempio n. 11
0
class ProductTemplate(models.Model):
    _inherit = "product.template"

    uos_id = fields.Many2one(
        'product.uom',
        'Unit of Sale',
        help='Specify a unit of measure here if invoicing is made in another'
        ' unit of measure than inventory. Keep empty to use the default unit of measure.'
    )
    uos_coeff = fields.Float(
        'Unit of Measure -> UOS Coeff',
        digits=dp.get_precision('Product Unit of Measure'),
        help='Coefficient to convert default Unit of Measure to Unit of Sale'
        ' uos = uom * coeff')
Esempio n. 12
0
class mrp_product_produce_line(osv.osv_memory):
    _name = "mrp.product.produce.line"
    _description = "Product Produce Consume lines"

    _columns = {
        'product_id':
        fields.many2one('product.product', string='Product'),
        'product_qty':
        fields.float(
            'Quantity (in default UoM)',
            digits_compute=dp.get_precision('Product Unit of Measure')),
        'lot_id':
        fields.many2one('stock.production.lot', string='Lot'),
        'produce_id':
        fields.many2one('mrp.product.produce', string="Produce"),
    }
Esempio n. 13
0
class stock_return_picking_line(osv.osv_memory):
    _name = "stock.return.picking.line"
    _rec_name = 'product_id'

    _columns = {
        'product_id':
        fields.many2one('product.product', string="Product", required=True),
        'quantity':
        fields.float(
            "Quantity",
            digits_compute=dp.get_precision('Product Unit of Measure'),
            required=True),
        'wizard_id':
        fields.many2one('stock.return.picking', string="Wizard"),
        'move_id':
        fields.many2one('stock.move', "Move"),
    }
Esempio n. 14
0
class bid_line_qty(osv.osv_memory):
    _name = "bid.line.qty"
    _description = "Change Bid line quantity"
    _columns = {
        'qty':
        fields.float(
            'Quantity',
            digits_compute=dp.get_precision('Product Unit of Measure'),
            required=True),
    }

    def change_qty(self, cr, uid, ids, context=None):
        active_ids = context and context.get('active_ids', [])
        data = self.browse(cr, uid, ids, context=context)[0]
        self.pool.get('purchase.order.line').write(
            cr, uid, active_ids, {'quantity_tendered': data.qty})
        return {'type': 'ir.actions.act_window_close'}
Esempio n. 15
0
class StockMove(models.Model):
    _inherit = 'stock.move'

    def _default_uom(self):
        uom_categ_id = self.env.ref('product.product_uom_categ_kgm').id
        return self.env['product.uom'].search(
            [('category_id', '=', uom_categ_id), ('factor', '=', 1)], limit=1)

    weight = fields.Float(compute='_cal_move_weight',
                          digits=dp.get_precision('Stock Weight'),
                          store=True)
    weight_uom_id = fields.Many2one(
        'product.uom',
        string='Unit of Measure',
        required=True,
        readonly=True,
        help=
        "Unit of Measure (Unit of Measure) is the unit of measurement for Weight",
        default=_default_uom)

    @api.depends('product_id', 'product_uom_qty', 'product_uom')
    def _cal_move_weight(self):
        for move in self.filtered(
                lambda moves: moves.product_id.weight > 0.00):
            move.weight = (move.product_qty * move.product_id.weight)

    @api.multi
    def action_confirm(self):
        """
            Pass the carrier to the picking from the sales order
            (Should also work in case of Phantom BoMs when on explosion the original move is deleted)
        """
        procs_to_check = []
        for move in self:
            if move.procurement_id and move.procurement_id.sale_line_id and move.procurement_id.sale_line_id.order_id.carrier_id:
                procs_to_check += [move.procurement_id]
        res = super(StockMove, self).action_confirm()
        for proc in procs_to_check:
            pickings = (proc.move_ids.mapped('picking_id')
                        ).filtered(lambda record: not record.carrier_id)
            if pickings:
                pickings.write(
                    {'carrier_id': proc.sale_line_id.order_id.carrier_id.id})
        return res
Esempio n. 16
0
class purchase_order_line(osv.osv):
    _inherit = 'purchase.order.line'

    _columns = {
        'quantity_tendered': fields.float('Quantity Tendered', digits_compute=dp.get_precision('Product Unit of Measure'), help="Technical field for not loosing the initial information about the quantity proposed in the tender", oldname='quantity_bid'),
    }

    def generate_po(self, cr, uid, tender_id, context=None):
        #call generate_po from tender with active_id. Called from js widget
        return self.pool.get('purchase.requisition').generate_po(cr, uid, [tender_id], context=context)

    def button_confirm(self, cr, uid, ids, context=None):
        for element in self.browse(cr, uid, ids, context=context):
            self.write(cr, uid, element.id, {'quantity_tendered': element.product_qty}, context=context)
        return True

    def button_cancel(self, cr, uid, ids, context=None):
        self.write(cr, uid, ids, {'quantity_tendered': 0}, context=context)
        return True
Esempio n. 17
0
class mrp_repair_fee(osv.osv, ProductChangeMixin):
    _name = 'mrp.repair.fee'
    _description = 'Repair Fees Line'

    def _amount_line(self, cr, uid, ids, field_name, arg, context=None):
        """ Calculates amount.
        @param field_name: Name of field.
        @param arg: Argument
        @return: Dictionary of values.
        """
        res = {}
        tax_obj = self.pool.get('account.tax')
        cur_obj = self.pool.get('res.currency')
        for line in self.browse(cr, uid, ids, context=context):
            if line.to_invoice:
                cur = line.repair_id.pricelist_id.currency_id
                taxes = tax_obj.compute_all(cr, uid, line.tax_id, line.price_unit, cur.id, line.product_uom_qty, line.product_id.id, line.repair_id.partner_id.id)
                res[line.id] = taxes['total_included']
            else:
                res[line.id] = 0
        return res

    _columns = {
        'repair_id': fields.many2one('mrp.repair', 'Repair Order Reference', required=True, ondelete='cascade', select=True),
        'name': fields.char('Description', select=True, required=True),
        'product_id': fields.many2one('product.product', 'Product'),
        'product_uom_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
        'price_unit': fields.float('Unit Price', required=True),
        'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True),
        'price_subtotal': fields.function(_amount_line, string='Subtotal', digits=0),
        'tax_id': fields.many2many('account.tax', 'repair_fee_line_tax', 'repair_fee_line_id', 'tax_id', 'Taxes'),
        'invoice_line_id': fields.many2one('account.invoice.line', 'Invoice Line', readonly=True, copy=False),
        'to_invoice': fields.boolean('To Invoice'),
        'invoiced': fields.boolean('Invoiced', readonly=True, copy=False),
    }

    _defaults = {
        'to_invoice': lambda *a: True,
    }
Esempio n. 18
0
class sale_order(osv.osv):
    _inherit = "sale.order"

    def _product_margin(self, cr, uid, ids, field_name, arg, context=None):
        result = {}
        for sale in self.browse(cr, uid, ids, context=context):
            result[sale.id] = 0.0
            for line in sale.order_line:
                if line.state == 'cancel':
                    continue
                result[sale.id] += line.margin or 0.0
        return result

    def _get_order(self, cr, uid, ids, context=None):
        result = {}
        for line in self.pool.get('sale.order.line').browse(cr,
                                                            uid,
                                                            ids,
                                                            context=context):
            result[line.order_id.id] = True
        return result.keys()

    _columns = {
        'margin':
        fields.function(
            _product_margin,
            string='Margin',
            help=
            "It gives profitability by calculating the difference between the Unit Price and the cost.",
            store={
                'sale.order.line': (_get_order, ['margin',
                                                 'purchase_price'], 20),
                'sale.order':
                (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 20),
            },
            digits_compute=dp.get_precision('Product Price')),
    }
Esempio n. 19
0
class stock_landed_cost_lines(osv.osv):
    _name = 'stock.landed.cost.lines'
    _description = 'Stock Landed Cost Lines'

    def onchange_product_id(self, cr, uid, ids, product_id=False, context=None):
        result = {}
        if not product_id:
            return {'value': {'quantity': 0.0, 'price_unit': 0.0}}

        product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
        result['name'] = product.name
        result['split_method'] = product.split_method
        result['price_unit'] = product.standard_price
        result['account_id'] = product.property_account_expense_id and product.property_account_expense_id.id or product.categ_id.property_account_expense_categ_id.id
        return {'value': result}

    _columns = {
        'name': fields.char('Description'),
        'cost_id': fields.many2one('stock.landed.cost', 'Landed Cost', required=True, ondelete='cascade'),
        'product_id': fields.many2one('product.product', 'Product', required=True),
        'price_unit': fields.float('Cost', required=True, digits_compute=dp.get_precision('Product Price')),
        'split_method': fields.selection(product.SPLIT_METHOD, string='Split Method', required=True),
        'account_id': fields.many2one('account.account', 'Account', domain=[('internal_type', '!=', 'view'), ('internal_type', '!=', 'closed'), ('deprecated', '=', False)]),
    }
Esempio n. 20
0
class SaleOrderLine(models.Model):
    _inherit = 'sale.order.line'

    @api.one
    def _set_uos(self):
        if self.product_id.uos_coeff:
            self.product_uom_qty = self.product_uos_qty / self.product_id.uos_coeff
            self.product_uom = self.product_id.uom_id

    @api.one
    def _compute_uos(self):
        self.product_uos_qty = self.product_uom_qty * self.product_id.uos_coeff

    product_uos_qty = fields.Float(
        string='Quantity',
        digits=dp.get_precision('Product Unit of Measure'),
        compute='_compute_uos',
        inverse='_set_uos',
        readonly=False)
    product_uos = fields.Many2one('product.uom',
                                  string='Unit of Measure',
                                  required=True,
                                  related='product_id.uos_id',
                                  readonly=True)
Esempio n. 21
0
class event_ticket(models.Model):
    _name = 'event.event.ticket'
    _description = 'Event Ticket'

    name = fields.Char('Name', required=True, translate=True)
    event_id = fields.Many2one('event.event',
                               "Event",
                               required=True,
                               ondelete='cascade')
    product_id = fields.Many2one(
        'product.product',
        'Product',
        required=True,
        domain=[("event_type_id", "!=", False)],
        default=lambda self: self._default_product_id())
    registration_ids = fields.One2many('event.registration', 'event_ticket_id',
                                       'Registrations')
    price = fields.Float('Price', digits=dp.get_precision('Product Price'))
    deadline = fields.Date("Sales End")
    is_expired = fields.Boolean('Is Expired', compute='_is_expired')

    @api.model
    def _default_product_id(self):
        try:
            product = self.env['ir.model.data'].get_object(
                'event_sale', 'product_product_event')
            return product.id
        except ValueError:
            return False

    @api.one
    @api.depends('deadline')
    def _is_expired(self):
        if self.deadline:
            current_date = fields.Date.context_today(
                self.with_context({'tz': self.event_id.date_tz}))
            self.is_expired = self.deadline < current_date
        else:
            self.is_expired = False

    # FIXME non-stored fields wont ends up in _columns (and thus _all_columns), which forbid them
    #       to be used in qweb views. Waiting a fix, we create an old function field directly.
    """
    price_reduce = fields.Float("Price Reduce", compute="_get_price_reduce", store=False,
                                digits=dp.get_precision('Product Price'))
    @api.one
    @api.depends('price', 'product_id.lst_price', 'product_id.price')
    def _get_price_reduce(self):
        product = self.product_id
        discount = product.lst_price and (product.lst_price - product.price) / product.lst_price or 0.0
        self.price_reduce = (1.0 - discount) * self.price
    """

    def _get_price_reduce(self, cr, uid, ids, field_name, arg, context=None):
        res = dict.fromkeys(ids, 0.0)
        for ticket in self.browse(cr, uid, ids, context=context):
            product = ticket.product_id
            discount = product.lst_price and (
                product.lst_price - product.price) / product.lst_price or 0.0
            res[ticket.id] = (1.0 - discount) * ticket.price
        return res

    _columns = {
        'price_reduce':
        old_fields.function(_get_price_reduce,
                            type='float',
                            string='Price Reduce',
                            digits_compute=dp.get_precision('Product Price')),
    }

    # seats fields
    seats_availability = fields.Selection([('limited', 'Limited'),
                                           ('unlimited', 'Unlimited')],
                                          'Available Seat',
                                          required=True,
                                          store=True,
                                          compute='_compute_seats',
                                          default="limited")
    seats_max = fields.Integer(
        'Maximum Available Seats',
        help=
        "Define the number of available tickets. If you have too much registrations you will "
        "not be able to sell tickets anymore. Set 0 to ignore this rule set as unlimited."
    )
    seats_reserved = fields.Integer(string='Reserved Seats',
                                    compute='_compute_seats',
                                    store=True)
    seats_available = fields.Integer(string='Available Seats',
                                     compute='_compute_seats',
                                     store=True)
    seats_unconfirmed = fields.Integer(string='Unconfirmed Seat Reservations',
                                       compute='_compute_seats',
                                       store=True)
    seats_used = fields.Integer(compute='_compute_seats', store=True)

    @api.multi
    @api.depends('seats_max', 'registration_ids.state')
    def _compute_seats(self):
        """ Determine reserved, available, reserved but unconfirmed and used seats. """
        # initialize fields to 0 + compute seats availability
        for ticket in self:
            ticket.seats_availability = 'unlimited' if ticket.seats_max == 0 else 'limited'
            ticket.seats_unconfirmed = ticket.seats_reserved = ticket.seats_used = ticket.seats_available = 0
        # aggregate registrations by ticket and by state
        if self.ids:
            state_field = {
                'draft': 'seats_unconfirmed',
                'open': 'seats_reserved',
                'done': 'seats_used',
            }
            query = """ SELECT event_ticket_id, state, count(event_id)
                        FROM event_registration
                        WHERE event_ticket_id IN %s AND state IN ('draft', 'open', 'done')
                        GROUP BY event_ticket_id, state
                    """
            self._cr.execute(query, (tuple(self.ids), ))
            for event_ticket_id, state, num in self._cr.fetchall():
                ticket = self.browse(event_ticket_id)
                ticket[state_field[state]] += num
        # compute seats_available
        for ticket in self:
            if ticket.seats_max > 0:
                ticket.seats_available = ticket.seats_max - (
                    ticket.seats_reserved + ticket.seats_used)

    @api.one
    @api.constrains('registration_ids', 'seats_max')
    def _check_seats_limit(self):
        if self.seats_max and self.seats_available < 0:
            raise UserError(_('No more available seats for the ticket'))

    @api.onchange('product_id')
    def onchange_product_id(self):
        price = self.product_id.list_price if self.product_id else 0
        return {'value': {'price': price}}
Esempio n. 22
0
class change_standard_price(osv.osv_memory):
    _name = "stock.change.standard.price"
    _description = "Change Standard Price"
    _columns = {
        'new_price':
        fields.float(
            'Price',
            required=True,
            digits_compute=dp.get_precision('Product Price'),
            help=
            "If cost price is increased, stock variation account will be debited "
            "and stock output account will be credited with the value = (difference of amount * quantity available).\n"
            "If cost price is decreased, stock variation account will be creadited and stock input account will be debited."
        ),
    }

    def default_get(self, cr, uid, fields, context=None):
        """ To get default values for the object.
         @param self: The object pointer.
         @param cr: A database cursor
         @param uid: ID of the user currently logged in
         @param fields: List of fields for which we want default values
         @param context: A standard dictionary
         @return: A dictionary which of fields with values.
        """
        if context is None:
            context = {}
        if context.get("active_model") == 'product.product':
            product_pool = self.pool.get('product.product')
        else:
            product_pool = self.pool.get('product.template')
        product_obj = product_pool.browse(cr, uid,
                                          context.get('active_id', False))

        res = super(change_standard_price, self).default_get(cr,
                                                             uid,
                                                             fields,
                                                             context=context)

        price = product_obj.standard_price

        if 'new_price' in fields:
            res.update({'new_price': price})
        return res

    def change_price(self, cr, uid, ids, context=None):
        """ Changes the Standard Price of Product.
            And creates an account move accordingly.
        @param self: The object pointer.
        @param cr: A database cursor
        @param uid: ID of the user currently logged in
        @param ids: List of IDs selected
        @param context: A standard dictionary
        @return:
        """
        if context is None:
            context = {}
        rec_id = context.get('active_id', False)
        assert rec_id, _('Active ID is not set in Context.')
        if context.get("active_model") == 'product.product':
            prod_obj = self.pool.get('product.product')
            rec_id = prod_obj.browse(cr, uid, rec_id,
                                     context=context).product_tmpl_id.id
        prod_obj = self.pool.get('product.template')

        res = self.browse(cr, uid, ids, context=context)

        prod_obj.do_change_standard_price(cr, uid, [rec_id], res[0].new_price,
                                          context)
        return {'type': 'ir.actions.act_window_close'}
Esempio n. 23
0
class product_pricelist_item(osv.osv):
    _name = "product.pricelist.item"
    _description = "Pricelist item"
    _order = "applied_on, min_quantity desc"

    def _check_recursion(self, cr, uid, ids, context=None):
        for obj_list in self.browse(cr, uid, ids, context=context):
            if obj_list.base == 'pricelist':
                main_pricelist = obj_list.pricelist_id.id
                other_pricelist = obj_list.base_pricelist_id.id
                if main_pricelist == other_pricelist:
                    return False
        return True

    def _check_margin(self, cr, uid, ids, context=None):
        for item in self.browse(cr, uid, ids, context=context):
            if item.price_max_margin and item.price_min_margin and (
                    item.price_min_margin > item.price_max_margin):
                return False
        return True

    _columns = {
        'product_tmpl_id': fields.many2one('product.template', 'Product Template', ondelete='cascade', help="Specify a template if this rule only applies to one product template. Keep empty otherwise."),
        'product_id': fields.many2one('product.product', 'Product', ondelete='cascade', help="Specify a product if this rule only applies to one product. Keep empty otherwise."),
        'categ_id': fields.many2one('product.category', 'Product Category', ondelete='cascade', help="Specify a product category if this rule only applies to products belonging to this category or its children categories. Keep empty otherwise."),
        'min_quantity': fields.integer('Min. Quantity',
            help="For the rule to apply, bought/sold quantity must be greater "
              "than or equal to the minimum quantity specified in this field.\n"
              "Expressed in the default unit of measure of the product."
            ),
        'applied_on': fields.selection([('3_global', 'Global'),('2_product_category', ' Product Category'), ('1_product', 'Product'), ('0_product_variant', 'Product Variant')], string="Apply On", required=True,
            help='Pricelist Item applicable on selected option'),
        'sequence': fields.integer('Sequence', required=True, help="Gives the order in which the pricelist items will be checked. The evaluation gives highest priority to lowest sequence and stops as soon as a matching item is found."),
        'base': fields.selection([('list_price', 'Public Price'), ('standard_price', 'Cost'), ('pricelist', 'Other Pricelist')], string="Based on", required=True,
            help='Base price for computation. \n Public Price: The base price will be the Sale/public Price. \n Cost Price : The base price will be the cost price. \n Other Pricelist : Computation of the base price based on another Pricelist.'),
        'base_pricelist_id': fields.many2one('product.pricelist', 'Other Pricelist'),
        'pricelist_id': fields.many2one('product.pricelist', 'Pricelist'),
        'price_surcharge': fields.float('Price Surcharge',
            digits_compute= dp.get_precision('Product Price'), help='Specify the fixed amount to add or substract(if negative) to the amount calculated with the discount.'),
        'price_discount': fields.float('Price Discount', digits=(16,2)),
        'price_round': fields.float('Price Rounding',
            digits_compute= dp.get_precision('Product Price'),
            help="Sets the price so that it is a multiple of this value.\n" \
              "Rounding is applied after the discount and before the surcharge.\n" \
              "To have prices that end in 9.99, set rounding 10, surcharge -0.01" \
            ),
        'price_min_margin': fields.float('Min. Price Margin',
            digits_compute= dp.get_precision('Product Price'), help='Specify the minimum amount of margin over the base price.'),
        'price_max_margin': fields.float('Max. Price Margin',
            digits_compute= dp.get_precision('Product Price'), help='Specify the maximum amount of margin over the base price.'),
        'company_id': fields.related('pricelist_id','company_id',type='many2one',
            readonly=True, relation='res.company', string='Company', store=True),
        'currency_id': fields.related('pricelist_id', 'currency_id', type='many2one',
            readonly=True, relation='res.currency', string='Currency', store=True),
        'date_start': fields.date('Start Date', help="Starting date for the pricelist item validation"),
        'date_end': fields.date('End Date', help="Ending valid for the pricelist item validation"),
        'compute_price': fields.selection([('fixed', 'Fix Price'), ('percentage', 'Percentage (discount)'), ('formula', 'Formula')], select=True, default='fixed'),
        'fixed_price': fields.float('Fixed Price'),
        'percent_price': fields.float('Percentage Price'),
    }

    _defaults = {
        'base': 'list_price',
        'min_quantity': 1,
        'sequence': 5,
        'price_discount': 0,
        'applied_on': '3_global',
    }
    _constraints = [
        (_check_recursion,
         'Error! You cannot assign the Main Pricelist as Other Pricelist in PriceList Item!',
         ['base_pricelist_id']),
        (_check_margin,
         'Error! The minimum margin should be lower than the maximum margin.',
         ['price_min_margin', 'price_max_margin'])
    ]
Esempio n. 24
0
class procurement_order(osv.osv):
    """
    Procurement Orders
    """
    _name = "procurement.order"
    _description = "Procurement"
    _order = 'priority desc, date_planned, id asc'
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _log_create = False
    _columns = {
        'name':
        fields.text('Description', required=True),
        'origin':
        fields.char(
            'Source Document',
            help="Reference of the document that created this Procurement.\n"
            "This is automatically completed by eCore."),
        'company_id':
        fields.many2one('res.company', 'Company', required=True),

        # These two fields are used for shceduling
        'priority':
        fields.selection(PROCUREMENT_PRIORITIES,
                         'Priority',
                         required=True,
                         select=True,
                         track_visibility='onchange'),
        'date_planned':
        fields.datetime('Scheduled Date',
                        required=True,
                        select=True,
                        track_visibility='onchange'),
        'group_id':
        fields.many2one('procurement.group', 'Procurement Group'),
        'rule_id':
        fields.many2one(
            'procurement.rule',
            'Rule',
            track_visibility='onchange',
            help=
            "Chosen rule for the procurement resolution. Usually chosen by the system but can be manually set by the procurement manager to force an unusual behavior."
        ),
        'product_id':
        fields.many2one('product.product',
                        'Product',
                        required=True,
                        states={'confirmed': [('readonly', False)]},
                        readonly=True),
        'product_qty':
        fields.float(
            'Quantity',
            digits_compute=dp.get_precision('Product Unit of Measure'),
            required=True,
            states={'confirmed': [('readonly', False)]},
            readonly=True),
        'product_uom':
        fields.many2one('product.uom',
                        'Product Unit of Measure',
                        required=True,
                        states={'confirmed': [('readonly', False)]},
                        readonly=True),
        'state':
        fields.selection([('cancel', 'Cancelled'), ('confirmed', 'Confirmed'),
                          ('exception', 'Exception'), ('running', 'Running'),
                          ('done', 'Done')],
                         'Status',
                         required=True,
                         track_visibility='onchange',
                         copy=False),
    }

    _defaults = {
        'state':
        'confirmed',
        'priority':
        '1',
        'date_planned':
        lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
        'company_id':
        lambda self, cr, uid, c: self.pool.get('res.company').
        _company_default_get(cr, uid, 'procurement.order', context=c)
    }

    def _needaction_domain_get(self, cr, uid, context=None):
        return [('state', '=', 'exception')]

    def unlink(self, cr, uid, ids, context=None):
        procurements = self.read(cr, uid, ids, ['state'], context=context)
        unlink_ids = []
        for s in procurements:
            if s['state'] == 'cancel':
                unlink_ids.append(s['id'])
            else:
                raise UserError(
                    _('Cannot delete Procurement Order(s) which are in %s state.'
                      ) % s['state'])
        return osv.osv.unlink(self, cr, uid, unlink_ids, context=context)

    def create(self, cr, uid, vals, context=None):
        context = context or {}
        procurement_id = super(procurement_order, self).create(cr,
                                                               uid,
                                                               vals,
                                                               context=context)
        if not context.get('procurement_autorun_defer'):
            self.run(cr, uid, [procurement_id], context=context)
        return procurement_id

    def do_view_procurements(self, cr, uid, ids, context=None):
        '''
        This function returns an action that display existing procurement orders
        of same procurement group of given ids.
        '''
        act_obj = self.pool.get('ir.actions.act_window')
        action_id = self.pool.get('ir.model.data').xmlid_to_res_id(
            cr,
            uid,
            'procurement.do_view_procurements',
            raise_if_not_found=True)
        result = act_obj.read(cr, uid, [action_id], context=context)[0]
        group_ids = set([
            proc.group_id.id
            for proc in self.browse(cr, uid, ids, context=context)
            if proc.group_id
        ])
        result['domain'] = "[('group_id','in',[" + ','.join(
            map(str, list(group_ids))) + "])]"
        return result

    def onchange_product_id(self, cr, uid, ids, product_id, context=None):
        """ Finds UoM of changed product.
        @param product_id: Changed id of product.
        @return: Dictionary of values.
        """
        if product_id:
            w = self.pool.get('product.product').browse(cr,
                                                        uid,
                                                        product_id,
                                                        context=context)
            v = {
                'product_uom': w.uom_id.id,
            }
            return {'value': v}
        return {}

    def get_cancel_ids(self, cr, uid, ids, context=None):
        return [
            proc.id for proc in self.browse(cr, uid, ids, context=context)
            if proc.state != 'done'
        ]

    def cancel(self, cr, uid, ids, context=None):
        #cancel only the procurements that aren't done already
        to_cancel_ids = self.get_cancel_ids(cr, uid, ids, context=context)
        if to_cancel_ids:
            return self.write(cr,
                              uid,
                              to_cancel_ids, {'state': 'cancel'},
                              context=context)

    def reset_to_confirmed(self, cr, uid, ids, context=None):
        return self.write(cr,
                          uid,
                          ids, {'state': 'confirmed'},
                          context=context)

    @api.v8
    def run(self, autocommit=False):
        return self._model.run(self._cr,
                               self._uid,
                               self.ids,
                               autocommit=False,
                               context=self._context)

    @api.v7
    def run(self, cr, uid, ids, autocommit=False, context=None):
        for procurement_id in ids:
            #we intentionnaly do the browse under the for loop to avoid caching all ids which would be resource greedy
            #and useless as we'll make a refresh later that will invalidate all the cache (and thus the next iteration
            #will fetch all the ids again)
            procurement = self.browse(cr, uid, procurement_id, context=context)
            if procurement.state not in ("running", "done"):
                try:
                    if self._assign(cr, uid, procurement, context=context):
                        res = self._run(cr,
                                        uid,
                                        procurement,
                                        context=context or {})
                        if res:
                            self.write(cr,
                                       uid, [procurement.id],
                                       {'state': 'running'},
                                       context=context)
                        else:
                            self.write(cr,
                                       uid, [procurement.id],
                                       {'state': 'exception'},
                                       context=context)
                    else:
                        self.message_post(
                            cr,
                            uid, [procurement.id],
                            body=_('No rule matching this procurement'),
                            context=context)
                        self.write(cr,
                                   uid, [procurement.id],
                                   {'state': 'exception'},
                                   context=context)
                    if autocommit:
                        cr.commit()
                except OperationalError:
                    if autocommit:
                        cr.rollback()
                        continue
                    else:
                        raise
        return True

    def check(self, cr, uid, ids, autocommit=False, context=None):
        done_ids = []
        for procurement in self.browse(cr, uid, ids, context=context):
            try:
                result = self._check(cr, uid, procurement, context=context)
                if result:
                    done_ids.append(procurement.id)
                if autocommit:
                    cr.commit()
            except OperationalError:
                if autocommit:
                    cr.rollback()
                    continue
                else:
                    raise
        if done_ids:
            self.write(cr, uid, done_ids, {'state': 'done'}, context=context)
        return done_ids

    #
    # Method to overwrite in different procurement modules
    #
    def _find_suitable_rule(self, cr, uid, procurement, context=None):
        '''This method returns a procurement.rule that depicts what to do with the given procurement
        in order to complete its needs. It returns False if no suiting rule is found.
            :param procurement: browse record
            :rtype: int or False
        '''
        return False

    def _assign(self, cr, uid, procurement, context=None):
        '''This method check what to do with the given procurement in order to complete its needs.
        It returns False if no solution is found, otherwise it stores the matching rule (if any) and
        returns True.
            :param procurement: browse record
            :rtype: boolean
        '''
        #if the procurement already has a rule assigned, we keep it (it has a higher priority as it may have been chosen manually)
        if procurement.rule_id:
            return True
        elif procurement.product_id.type not in ('service', 'digital'):
            rule_id = self._find_suitable_rule(cr,
                                               uid,
                                               procurement,
                                               context=context)
            if rule_id:
                self.write(cr,
                           uid, [procurement.id], {'rule_id': rule_id},
                           context=context)
                return True
        return False

    def _run(self, cr, uid, procurement, context=None):
        '''This method implements the resolution of the given procurement
            :param procurement: browse record
            :returns: True if the resolution of the procurement was a success, False otherwise to set it in exception
        '''
        return True

    def _check(self, cr, uid, procurement, context=None):
        '''Returns True if the given procurement is fulfilled, False otherwise
            :param procurement: browse record
            :rtype: boolean
        '''
        return False

    #
    # Scheduler
    #
    def run_scheduler(self,
                      cr,
                      uid,
                      use_new_cursor=False,
                      company_id=False,
                      context=None):
        '''
        Call the scheduler to check the procurement order. This is intented to be done for all existing companies at
        the same time, so we're running all the methods as SUPERUSER to avoid intercompany and access rights issues.

        @param self: The object pointer
        @param cr: The current row, from the database cursor,
        @param uid: The current user ID for security checks
        @param ids: List of selected IDs
        @param use_new_cursor: if set, use a dedicated cursor and auto-commit after processing each procurement.
            This is appropriate for batch jobs only.
        @param context: A standard dictionary for contextual values
        @return:  Dictionary of values
        '''
        if context is None:
            context = {}
        try:
            if use_new_cursor:
                cr = ecore.registry(cr.dbname).cursor()

            # Run confirmed procurements
            dom = [('state', '=', 'confirmed')]
            if company_id:
                dom += [('company_id', '=', company_id)]
            prev_ids = []
            while True:
                ids = self.search(cr, SUPERUSER_ID, dom, context=context)
                if not ids or prev_ids == ids:
                    break
                else:
                    prev_ids = ids
                self.run(cr,
                         SUPERUSER_ID,
                         ids,
                         autocommit=use_new_cursor,
                         context=context)
                if use_new_cursor:
                    cr.commit()

            # Check if running procurements are done
            offset = 0
            dom = [('state', '=', 'running')]
            if company_id:
                dom += [('company_id', '=', company_id)]
            prev_ids = []
            while True:
                ids = self.search(cr,
                                  SUPERUSER_ID,
                                  dom,
                                  offset=offset,
                                  context=context)
                if not ids or prev_ids == ids:
                    break
                else:
                    prev_ids = ids
                self.check(cr,
                           SUPERUSER_ID,
                           ids,
                           autocommit=use_new_cursor,
                           context=context)
                if use_new_cursor:
                    cr.commit()

        finally:
            if use_new_cursor:
                try:
                    cr.close()
                except Exception:
                    pass

        return {}
Esempio n. 25
0
class product_template(osv.osv):
    _name = 'product.template'
    _inherit = 'product.template'

    def _product_available(self, cr, uid, ids, name, arg, context=None):
        prod_available = {}
        product_ids = self.browse(cr, uid, ids, context=context)
        var_ids = []
        for product in product_ids:
            var_ids += [p.id for p in product.product_variant_ids]
        variant_available = self.pool['product.product']._product_available(
            cr, uid, var_ids, context=context)

        for product in product_ids:
            qty_available = 0
            virtual_available = 0
            incoming_qty = 0
            outgoing_qty = 0
            for p in product.product_variant_ids:
                qty_available += variant_available[p.id]["qty_available"]
                virtual_available += variant_available[
                    p.id]["virtual_available"]
                incoming_qty += variant_available[p.id]["incoming_qty"]
                outgoing_qty += variant_available[p.id]["outgoing_qty"]
            prod_available[product.id] = {
                "qty_available": qty_available,
                "virtual_available": virtual_available,
                "incoming_qty": incoming_qty,
                "outgoing_qty": outgoing_qty,
            }
        return prod_available

    def _search_product_quantity(self, cr, uid, obj, name, domain, context):
        prod = self.pool.get("product.product")
        product_variant_ids = prod.search(cr, uid, domain, context=context)
        return [('product_variant_ids', 'in', product_variant_ids)]

    def _product_available_text(self,
                                cr,
                                uid,
                                ids,
                                field_names=None,
                                arg=False,
                                context=None):
        res = {}
        for product in self.browse(cr, uid, ids, context=context):
            res[product.id] = str(product.qty_available) + _(" On Hand")
        return res

    def _compute_nbr_reordering_rules(self,
                                      cr,
                                      uid,
                                      ids,
                                      field_names=None,
                                      arg=None,
                                      context=None):
        res = dict.fromkeys(
            ids, {
                'nbr_reordering_rules': 0,
                'reordering_min_qty': 0,
                'reordering_max_qty': 0
            })
        product_data = self.pool['stock.warehouse.orderpoint'].read_group(
            cr,
            uid, [('product_id.product_tmpl_id', 'in', ids)],
            ['product_id', 'product_min_qty', 'product_max_qty'],
            ['product_id'],
            context=context)
        for data in product_data:
            product_tmpl_id = data['__domain'][1][2][0]
            res[product_tmpl_id][
                'nbr_reordering_rules'] = res[product_tmpl_id].get(
                    'nbr_reordering_rules', 0) + int(data['product_id_count'])
            res[product_tmpl_id]['reordering_min_qty'] = data[
                'product_min_qty']
            res[product_tmpl_id]['reordering_max_qty'] = data[
                'product_max_qty']
        return res

    def _get_product_template_type(self, cr, uid, context=None):
        res = super(product_template,
                    self)._get_product_template_type(cr, uid, context=context)
        if 'product' not in [item[0] for item in res]:
            res.append(('product', 'Producto Almacenable'))
        return res

    _columns = {
        'property_stock_procurement':
        fields.property(
            type='many2one',
            relation='stock.location',
            string="Procurement Location",
            domain=[('usage', 'like', 'procurement')],
            help=
            "This stock location will be used, instead of the default one, as the source location for stock moves generated by procurements."
        ),
        'property_stock_production':
        fields.property(
            type='many2one',
            relation='stock.location',
            string="Production Location",
            domain=[('usage', 'like', 'production')],
            help=
            "This stock location will be used, instead of the default one, as the source location for stock moves generated by manufacturing orders."
        ),
        'property_stock_inventory':
        fields.property(
            type='many2one',
            relation='stock.location',
            string="Inventory Location",
            domain=[('usage', 'like', 'inventory')],
            help=
            "This stock location will be used, instead of the default one, as the source location for stock moves generated when you do an inventory."
        ),
        'sale_delay':
        fields.float(
            'Customer Lead Time',
            help=
            "The average delay in days between the confirmation of the customer order and the delivery of the finished products. It's the time you promise to your customers."
        ),
        'tracking':
        fields.selection(selection=[('serial', 'By Unique Serial Number'),
                                    ('lot', 'By Lots'),
                                    ('none', 'No Tracking')],
                         string="Tracking",
                         required=True),
        'description_picking':
        fields.text('Description on Picking', translate=True),
        # sum of product variant qty
        # 'reception_count': fields.function(_product_available, multi='qty_available',
        #     fnct_search=_search_product_quantity, type='float', string='Quantity On Hand'),
        # 'delivery_count': fields.function(_product_available, multi='qty_available',
        #     fnct_search=_search_product_quantity, type='float', string='Quantity On Hand'),
        'qty_available':
        fields.function(
            _product_available,
            multi='qty_available',
            digits_compute=dp.get_precision('Product Unit of Measure'),
            fnct_search=_search_product_quantity,
            type='float',
            string='Quantity On Hand'),
        'virtual_available':
        fields.function(
            _product_available,
            multi='qty_available',
            digits_compute=dp.get_precision('Product Unit of Measure'),
            fnct_search=_search_product_quantity,
            type='float',
            string='Forecasted Quantity'),
        'incoming_qty':
        fields.function(
            _product_available,
            multi='qty_available',
            digits_compute=dp.get_precision('Product Unit of Measure'),
            fnct_search=_search_product_quantity,
            type='float',
            string='Incoming'),
        'outgoing_qty':
        fields.function(
            _product_available,
            multi='qty_available',
            digits_compute=dp.get_precision('Product Unit of Measure'),
            fnct_search=_search_product_quantity,
            type='float',
            string='Outgoing'),
        'location_id':
        fields.dummy(string='Location',
                     relation='stock.location',
                     type='many2one'),
        'warehouse_id':
        fields.dummy(string='Warehouse',
                     relation='stock.warehouse',
                     type='many2one'),
        'route_ids':
        fields.many2many(
            'stock.location.route',
            'stock_route_product',
            'product_id',
            'route_id',
            'Routes',
            domain="[('product_selectable', '=', True)]",
            help=
            "Depending on the modules installed, this will allow you to define the route of the product: whether it will be bought, manufactured, MTO/MTS,..."
        ),
        'nbr_reordering_rules':
        fields.function(_compute_nbr_reordering_rules,
                        string='Reordering Rules',
                        type='integer',
                        multi=True),
        'reordering_min_qty':
        fields.function(_compute_nbr_reordering_rules,
                        type='float',
                        multi=True),
        'reordering_max_qty':
        fields.function(_compute_nbr_reordering_rules,
                        type='float',
                        multi=True),
        'route_from_categ_ids':
        fields.related('categ_id',
                       'total_route_ids',
                       type="many2many",
                       relation="stock.location.route",
                       string="Category Routes"),
    }

    _defaults = {
        'sale_delay': 7,
        'tracking': 'none',
    }

    def action_view_routes(self, cr, uid, ids, context=None):
        route_obj = self.pool.get("stock.location.route")
        act_obj = self.pool.get('ir.actions.act_window')
        mod_obj = self.pool.get('ir.model.data')
        product_route_ids = set()
        for product in self.browse(cr, uid, ids, context=context):
            product_route_ids |= set([r.id for r in product.route_ids])
            product_route_ids |= set(
                [r.id for r in product.categ_id.total_route_ids])
        route_ids = route_obj.search(cr,
                                     uid, [
                                         '|',
                                         ('id', 'in', list(product_route_ids)),
                                         ('warehouse_selectable', '=', True)
                                     ],
                                     context=context)
        result = mod_obj.xmlid_to_res_id(cr,
                                         uid,
                                         'stock.action_routes_form',
                                         raise_if_not_found=True)
        result = act_obj.read(cr, uid, [result], context=context)[0]
        result['domain'] = "[('id','in',[" + ','.join(map(str,
                                                          route_ids)) + "])]"
        return result

    def onchange_tracking(self, cr, uid, ids, tracking, context=None):
        if not tracking:
            return {}
        product_product = self.pool['product.product']
        variant_ids = product_product.search(cr,
                                             uid,
                                             [('product_tmpl_id', 'in', ids)],
                                             context=context)
        return product_product.onchange_tracking(cr,
                                                 uid,
                                                 variant_ids,
                                                 tracking,
                                                 context=context)

    def _get_products(self, cr, uid, ids, context=None):
        products = []
        for prodtmpl in self.browse(cr, uid, ids, context=None):
            products += [x.id for x in prodtmpl.product_variant_ids]
        return products

    def _get_act_window_dict(self, cr, uid, name, context=None):
        mod_obj = self.pool.get('ir.model.data')
        act_obj = self.pool.get('ir.actions.act_window')
        result = mod_obj.xmlid_to_res_id(cr,
                                         uid,
                                         name,
                                         raise_if_not_found=True)
        result = act_obj.read(cr, uid, [result], context=context)[0]
        return result

    def action_open_quants(self, cr, uid, ids, context=None):
        products = self._get_products(cr, uid, ids, context=context)
        result = self._get_act_window_dict(cr,
                                           uid,
                                           'stock.product_open_quants',
                                           context=context)
        result['domain'] = "[('product_id','in',[" + ','.join(
            map(str, products)) + "])]"
        result[
            'context'] = "{'search_default_locationgroup': 1, 'search_default_internal_loc': 1}"
        return result

    def action_view_orderpoints(self, cr, uid, ids, context=None):
        products = self._get_products(cr, uid, ids, context=context)
        result = self._get_act_window_dict(cr,
                                           uid,
                                           'stock.product_open_orderpoint',
                                           context=context)
        if len(ids) == 1 and len(products) == 1:
            result['context'] = "{'default_product_id': " + str(
                products[0]) + ", 'search_default_product_id': " + str(
                    products[0]) + "}"
        else:
            result['domain'] = "[('product_id','in',[" + ','.join(
                map(str, products)) + "])]"
            result['context'] = "{}"
        return result

    def action_view_stock_moves(self, cr, uid, ids, context=None):
        products = self._get_products(cr, uid, ids, context=context)
        result = self._get_act_window_dict(cr,
                                           uid,
                                           'stock.act_product_stock_move_open',
                                           context=context)
        if products:
            result['context'] = "{'default_product_id': %d}" % products[0]
        result['domain'] = "[('product_id.product_tmpl_id','in',[" + ','.join(
            map(str, ids)) + "])]"
        return result

    def write(self, cr, uid, ids, vals, context=None):
        if 'uom_id' in vals:
            new_uom = self.pool.get('product.uom').browse(cr,
                                                          uid,
                                                          vals['uom_id'],
                                                          context=context)
            for product in self.browse(cr, uid, ids, context=context):
                old_uom = product.uom_id
                if old_uom != new_uom:
                    if self.pool.get('stock.move').search(
                            cr,
                            uid,
                        [('product_id', 'in',
                          [x.id for x in product.product_variant_ids]),
                         ('state', '=', 'done')],
                            limit=1,
                            context=context):
                        raise UserError(
                            _("You can not change the unit of measure of a product that has already been used in a done stock move. If you need to change the unit of measure, you may deactivate this product."
                              ))
        return super(product_template, self).write(cr,
                                                   uid,
                                                   ids,
                                                   vals,
                                                   context=context)
Esempio n. 26
0
class product_product(osv.osv):
    _inherit = "product.product"

    def _stock_move_count(self, cr, uid, ids, field_name, arg, context=None):
        res = dict([(id, {
            'reception_count': 0,
            'delivery_count': 0
        }) for id in ids])
        move_pool = self.pool.get('stock.move')
        moves = move_pool.read_group(
            cr, uid, [('product_id', 'in', ids),
                      ('location_id.usage', '!=', 'internal'),
                      ('location_dest_id.usage', '=', 'internal'),
                      ('state', 'in', ('confirmed', 'assigned', 'pending'))],
            ['product_id'], ['product_id'])
        for move in moves:
            product_id = move['product_id'][0]
            res[product_id]['reception_count'] = move['product_id_count']
        moves = move_pool.read_group(
            cr, uid, [('product_id', 'in', ids),
                      ('location_id.usage', '=', 'internal'),
                      ('location_dest_id.usage', '!=', 'internal'),
                      ('state', 'in', ('confirmed', 'assigned', 'pending'))],
            ['product_id'], ['product_id'])
        for move in moves:
            product_id = move['product_id'][0]
            res[product_id]['delivery_count'] = move['product_id_count']
        return res

    def view_header_get(self, cr, user, view_id, view_type, context=None):
        if context is None:
            context = {}
        res = super(product_product,
                    self).view_header_get(cr, user, view_id, view_type,
                                          context)
        if res: return res
        if (context.get('active_id', False)) and (context.get('active_model')
                                                  == 'stock.location'):
            return _('Products: ') + self.pool.get('stock.location').browse(
                cr, user, context['active_id'], context).name
        return res

    def _get_domain_locations(self, cr, uid, ids, context=None):
        '''
        Parses the context and returns a list of location_ids based on it.
        It will return all stock locations when no parameters are given
        Possible parameters are shop, warehouse, location, force_company, compute_child
        '''
        context = context or {}

        location_obj = self.pool.get('stock.location')
        warehouse_obj = self.pool.get('stock.warehouse')

        location_ids = []
        if context.get('location', False):
            if isinstance(context['location'], (int, long)):
                location_ids = [context['location']]
            elif isinstance(context['location'], basestring):
                domain = [('complete_name', 'ilike', context['location'])]
                if context.get('force_company', False):
                    domain += [('company_id', '=', context['force_company'])]
                location_ids = location_obj.search(cr,
                                                   uid,
                                                   domain,
                                                   context=context)
            else:
                location_ids = context['location']
        else:
            if context.get('warehouse', False):
                if isinstance(context['warehouse'], (int, long)):
                    wids = [context['warehouse']]
                elif isinstance(context['warehouse'], basestring):
                    domain = [('name', 'ilike', context['warehouse'])]
                    if context.get('force_company', False):
                        domain += [('company_id', '=',
                                    context['force_company'])]
                    wids = warehouse_obj.search(cr,
                                                uid,
                                                domain,
                                                context=context)
                else:
                    wids = context['warehouse']
            else:
                wids = warehouse_obj.search(cr, uid, [], context=context)

            for w in warehouse_obj.browse(cr, uid, wids, context=context):
                location_ids.append(w.view_location_id.id)

        operator = context.get('compute_child', True) and 'child_of' or 'in'
        domain = context.get('force_company', False) and [
            '&', ('company_id', '=', context['force_company'])
        ] or []
        locations = location_obj.browse(cr, uid, location_ids, context=context)
        if operator == "child_of" and locations and locations[
                0].parent_left != 0:
            loc_domain = []
            dest_loc_domain = []
            for loc in locations:
                if loc_domain:
                    loc_domain = ['|'] + loc_domain + [
                        '&',
                        ('location_id.parent_left', '>=', loc.parent_left),
                        ('location_id.parent_left', '<', loc.parent_right)
                    ]
                    dest_loc_domain = ['|'] + dest_loc_domain + [
                        '&',
                        ('location_dest_id.parent_left', '>=',
                         loc.parent_left),
                        ('location_dest_id.parent_left', '<', loc.parent_right)
                    ]
                else:
                    loc_domain += [
                        '&',
                        ('location_id.parent_left', '>=', loc.parent_left),
                        ('location_id.parent_left', '<', loc.parent_right)
                    ]
                    dest_loc_domain += [
                        '&',
                        ('location_dest_id.parent_left', '>=',
                         loc.parent_left),
                        ('location_dest_id.parent_left', '<', loc.parent_right)
                    ]

            return (domain + loc_domain,
                    domain + ['&'] + dest_loc_domain + ['!'] + loc_domain,
                    domain + ['&'] + loc_domain + ['!'] + dest_loc_domain)
        else:
            return (domain + [('location_id', operator, location_ids)],
                    domain + [
                        '&', ('location_dest_id', operator, location_ids), '!',
                        ('location_id', operator, location_ids)
                    ], domain + [
                        '&', ('location_id', operator, location_ids), '!',
                        ('location_dest_id', operator, location_ids)
                    ])

    def _get_domain_dates(self, cr, uid, ids, context):
        from_date = context.get('from_date', False)
        to_date = context.get('to_date', False)
        domain = []
        if from_date:
            domain.append(('date', '>=', from_date))
        if to_date:
            domain.append(('date', '<=', to_date))
        return domain

    def _product_available(self,
                           cr,
                           uid,
                           ids,
                           field_names=None,
                           arg=False,
                           context=None):
        context = context or {}
        field_names = field_names or []

        domain_products = [('product_id', 'in', ids)]
        domain_quant, domain_move_in, domain_move_out = [], [], []
        domain_quant_loc, domain_move_in_loc, domain_move_out_loc = self._get_domain_locations(
            cr, uid, ids, context=context)
        domain_move_in += self._get_domain_dates(
            cr, uid, ids, context=context) + [
                ('state', 'not in', ('done', 'cancel', 'draft'))
            ] + domain_products
        domain_move_out += self._get_domain_dates(
            cr, uid, ids, context=context) + [
                ('state', 'not in', ('done', 'cancel', 'draft'))
            ] + domain_products
        domain_quant += domain_products

        if context.get('lot_id'):
            domain_quant.append(('lot_id', '=', context['lot_id']))
        if context.get('owner_id'):
            domain_quant.append(('owner_id', '=', context['owner_id']))
            owner_domain = ('restrict_partner_id', '=', context['owner_id'])
            domain_move_in.append(owner_domain)
            domain_move_out.append(owner_domain)
        if context.get('package_id'):
            domain_quant.append(('package_id', '=', context['package_id']))

        domain_move_in += domain_move_in_loc
        domain_move_out += domain_move_out_loc
        moves_in = self.pool.get('stock.move').read_group(
            cr,
            uid,
            domain_move_in, ['product_id', 'product_qty'], ['product_id'],
            context=context)
        moves_out = self.pool.get('stock.move').read_group(
            cr,
            uid,
            domain_move_out, ['product_id', 'product_qty'], ['product_id'],
            context=context)

        domain_quant += domain_quant_loc
        quants = self.pool.get('stock.quant').read_group(cr,
                                                         uid,
                                                         domain_quant,
                                                         ['product_id', 'qty'],
                                                         ['product_id'],
                                                         context=context)
        quants = dict(map(lambda x: (x['product_id'][0], x['qty']), quants))

        moves_in = dict(
            map(lambda x: (x['product_id'][0], x['product_qty']), moves_in))
        moves_out = dict(
            map(lambda x: (x['product_id'][0], x['product_qty']), moves_out))
        res = {}
        for product in self.browse(cr, uid, ids, context=context):
            id = product.id
            qty_available = float_round(
                quants.get(id, 0.0),
                precision_rounding=product.uom_id.rounding)
            incoming_qty = float_round(
                moves_in.get(id, 0.0),
                precision_rounding=product.uom_id.rounding)
            outgoing_qty = float_round(
                moves_out.get(id, 0.0),
                precision_rounding=product.uom_id.rounding)
            virtual_available = float_round(
                quants.get(id, 0.0) + moves_in.get(id, 0.0) -
                moves_out.get(id, 0.0),
                precision_rounding=product.uom_id.rounding)
            res[id] = {
                'qty_available': qty_available,
                'incoming_qty': incoming_qty,
                'outgoing_qty': outgoing_qty,
                'virtual_available': virtual_available,
            }
        return res

    def _search_product_quantity(self, cr, uid, obj, name, domain, context):
        res = []
        for field, operator, value in domain:
            #to prevent sql injections
            assert field in ('qty_available', 'virtual_available',
                             'incoming_qty',
                             'outgoing_qty'), 'Invalid domain left operand'
            assert operator in ('<', '>', '=', '!=', '<=',
                                '>='), 'Invalid domain operator'
            assert isinstance(value,
                              (float, int)), 'Invalid domain right operand'

            if operator == '=':
                operator = '=='

            ids = []
            if name == 'qty_available' and (value != 0.0 or operator
                                            not in ('==', '>=', '<=')):
                res.append(('id', 'in',
                            self._search_qty_available(cr, uid, operator,
                                                       value, context)))
            else:
                product_ids = self.search(cr, uid, [], context=context)
                if product_ids:
                    #TODO: Still optimization possible when searching virtual quantities
                    for element in self.browse(cr,
                                               uid,
                                               product_ids,
                                               context=context):
                        if eval(str(element[field]) + operator + str(value)):
                            ids.append(element.id)
                    res.append(('id', 'in', ids))
        return res

    def _search_qty_available(self, cr, uid, operator, value, context):
        domain_quant = []
        if context.get('lot_id'):
            domain_quant.append(('lot_id', '=', context['lot_id']))
        if context.get('owner_id'):
            domain_quant.append(('owner_id', '=', context['owner_id']))
        if context.get('package_id'):
            domain_quant.append(('package_id', '=', context['package_id']))
        domain_quant += self._get_domain_locations(cr,
                                                   uid, [],
                                                   context=context)[0]
        quants = self.pool.get('stock.quant').read_group(cr,
                                                         uid,
                                                         domain_quant,
                                                         ['product_id', 'qty'],
                                                         ['product_id'],
                                                         context=context)
        quants = dict(map(lambda x: (x['product_id'][0], x['qty']), quants))
        quants = dict((k, v) for k, v in quants.iteritems()
                      if eval(str(v) + operator + str(value)))
        return (list(quants))

    def _product_available_text(self,
                                cr,
                                uid,
                                ids,
                                field_names=None,
                                arg=False,
                                context=None):
        res = {}
        for product in self.browse(cr, uid, ids, context=context):
            res[product.id] = str(product.qty_available) + _(" On Hand")
        return res

    def _compute_nbr_reordering_rules(self,
                                      cr,
                                      uid,
                                      ids,
                                      field_names=None,
                                      arg=None,
                                      context=None):
        res = dict.fromkeys(
            ids, {
                'nbr_reordering_rules': 0,
                'reordering_min_qty': 0,
                'reordering_max_qty': 0
            })
        product_data = self.pool['stock.warehouse.orderpoint'].read_group(
            cr,
            uid, [('product_id', 'in', ids)],
            ['product_id', 'product_min_qty', 'product_max_qty'],
            ['product_id'],
            context=context)
        for data in product_data:
            res[data['product_id'][0]]['nbr_reordering_rules'] = int(
                data['product_id_count'])
            res[data['product_id']
                [0]]['reordering_min_qty'] = data['product_min_qty']
            res[data['product_id']
                [0]]['reordering_max_qty'] = data['product_max_qty']
        return res

    _columns = {
        'reception_count':
        fields.function(_stock_move_count,
                        string="Receipt",
                        type='integer',
                        multi='pickings'),
        'delivery_count':
        fields.function(_stock_move_count,
                        string="Delivery",
                        type='integer',
                        multi='pickings'),
        'qty_available':
        fields.function(
            _product_available,
            multi='qty_available',
            type='float',
            digits_compute=dp.get_precision('Product Unit of Measure'),
            string='Quantity On Hand',
            fnct_search=_search_product_quantity,
            help="Current quantity of products.\n"
            "In a context with a single Stock Location, this includes "
            "goods stored at this Location, or any of its children.\n"
            "In a context with a single Warehouse, this includes "
            "goods stored in the Stock Location of this Warehouse, or any "
            "of its children.\n"
            "stored in the Stock Location of the Warehouse of this Shop, "
            "or any of its children.\n"
            "Otherwise, this includes goods stored in any Stock Location "
            "with 'internal' type."),
        'virtual_available':
        fields.function(
            _product_available,
            multi='qty_available',
            type='float',
            digits_compute=dp.get_precision('Product Unit of Measure'),
            string='Forecast Quantity',
            fnct_search=_search_product_quantity,
            help="Forecast quantity (computed as Quantity On Hand "
            "- Outgoing + Incoming)\n"
            "In a context with a single Stock Location, this includes "
            "goods stored in this location, or any of its children.\n"
            "In a context with a single Warehouse, this includes "
            "goods stored in the Stock Location of this Warehouse, or any "
            "of its children.\n"
            "Otherwise, this includes goods stored in any Stock Location "
            "with 'internal' type."),
        'incoming_qty':
        fields.function(
            _product_available,
            multi='qty_available',
            type='float',
            digits_compute=dp.get_precision('Product Unit of Measure'),
            string='Incoming',
            fnct_search=_search_product_quantity,
            help="Quantity of products that are planned to arrive.\n"
            "In a context with a single Stock Location, this includes "
            "goods arriving to this Location, or any of its children.\n"
            "In a context with a single Warehouse, this includes "
            "goods arriving to the Stock Location of this Warehouse, or "
            "any of its children.\n"
            "Otherwise, this includes goods arriving to any Stock "
            "Location with 'internal' type."),
        'outgoing_qty':
        fields.function(
            _product_available,
            multi='qty_available',
            type='float',
            digits_compute=dp.get_precision('Product Unit of Measure'),
            string='Outgoing',
            fnct_search=_search_product_quantity,
            help="Quantity of products that are planned to leave.\n"
            "In a context with a single Stock Location, this includes "
            "goods leaving this Location, or any of its children.\n"
            "In a context with a single Warehouse, this includes "
            "goods leaving the Stock Location of this Warehouse, or "
            "any of its children.\n"
            "Otherwise, this includes goods leaving any Stock "
            "Location with 'internal' type."),
        'orderpoint_ids':
        fields.one2many('stock.warehouse.orderpoint', 'product_id',
                        'Minimum Stock Rules'),
        'nbr_reordering_rules':
        fields.function(_compute_nbr_reordering_rules,
                        string='Reordering Rules',
                        type='integer',
                        multi=True),
        'reordering_min_qty':
        fields.function(_compute_nbr_reordering_rules,
                        type='float',
                        multi=True),
        'reordering_max_qty':
        fields.function(_compute_nbr_reordering_rules,
                        type='float',
                        multi=True),
    }

    def fields_view_get(self,
                        cr,
                        uid,
                        view_id=None,
                        view_type='form',
                        context=None,
                        toolbar=False,
                        submenu=False):
        res = super(product_product, self).fields_view_get(cr,
                                                           uid,
                                                           view_id=view_id,
                                                           view_type=view_type,
                                                           context=context,
                                                           toolbar=toolbar,
                                                           submenu=submenu)
        if context is None:
            context = {}
        if context.get('location') and isinstance(context['location'], int):
            location_info = self.pool.get('stock.location').browse(
                cr, uid, context['location'])
            fields = res.get('fields', {})
            if fields:
                if location_info.usage == 'supplier':
                    if fields.get('virtual_available'):
                        res['fields']['virtual_available']['string'] = _(
                            'Future Receipts')
                    if fields.get('qty_available'):
                        res['fields']['qty_available']['string'] = _(
                            'Received Qty')

                if location_info.usage == 'internal':
                    if fields.get('virtual_available'):
                        res['fields']['virtual_available']['string'] = _(
                            'Forecasted Quantity')

                if location_info.usage == 'customer':
                    if fields.get('virtual_available'):
                        res['fields']['virtual_available']['string'] = _(
                            'Future Deliveries')
                    if fields.get('qty_available'):
                        res['fields']['qty_available']['string'] = _(
                            'Delivered Qty')

                if location_info.usage == 'inventory':
                    if fields.get('virtual_available'):
                        res['fields']['virtual_available']['string'] = _(
                            'Future P&L')
                    if fields.get('qty_available'):
                        res['fields']['qty_available']['string'] = _('P&L Qty')

                if location_info.usage == 'procurement':
                    if fields.get('virtual_available'):
                        res['fields']['virtual_available']['string'] = _(
                            'Future Qty')
                    if fields.get('qty_available'):
                        res['fields']['qty_available']['string'] = _(
                            'Unplanned Qty')

                if location_info.usage == 'production':
                    if fields.get('virtual_available'):
                        res['fields']['virtual_available']['string'] = _(
                            'Future Productions')
                    if fields.get('qty_available'):
                        res['fields']['qty_available']['string'] = _(
                            'Produced Qty')
        return res

    def action_view_routes(self, cr, uid, ids, context=None):
        template_obj = self.pool.get("product.template")
        templ_ids = list(
            set([
                x.product_tmpl_id.id
                for x in self.browse(cr, uid, ids, context=context)
            ]))
        return template_obj.action_view_routes(cr,
                                               uid,
                                               templ_ids,
                                               context=context)

    def onchange_tracking(self, cr, uid, ids, tracking, context=None):
        if not tracking or tracking == 'none':
            return {}
        unassigned_quants = self.pool['stock.quant'].search_count(
            cr,
            uid, [('product_id', 'in', ids), ('lot_id', '=', False),
                  ('location_id.usage', '=', 'internal')],
            context=context)
        if unassigned_quants:
            return {
                'warning': {
                    'title':
                    _('Warning!'),
                    'message':
                    _("You have products in stock that have no lot number.  You can assign serial numbers by doing an inventory.  "
                      )
                }
            }
        return {}
Esempio n. 27
0
    def compute_landed_cost(self, cr, uid, ids, context=None):
        line_obj = self.pool.get('stock.valuation.adjustment.lines')
        unlink_ids = line_obj.search(cr, uid, [('cost_id', 'in', ids)], context=context)
        line_obj.unlink(cr, uid, unlink_ids, context=context)
        digits = dp.get_precision('Product Price')(cr)
        towrite_dict = {}
        for cost in self.browse(cr, uid, ids, context=None):
            if not cost.picking_ids:
                continue
            picking_ids = [p.id for p in cost.picking_ids]
            total_qty = 0.0
            total_cost = 0.0
            total_weight = 0.0
            total_volume = 0.0
            total_line = 0.0
            vals = self.get_valuation_lines(cr, uid, [cost.id], picking_ids=picking_ids, context=context)
            for v in vals:
                for line in cost.cost_lines:
                    v.update({'cost_id': cost.id, 'cost_line_id': line.id})
                    self.pool.get('stock.valuation.adjustment.lines').create(cr, uid, v, context=context)
                total_qty += v.get('quantity', 0.0)
                total_cost += v.get('former_cost', 0.0)
                total_weight += v.get('weight', 0.0)
                total_volume += v.get('volume', 0.0)
                total_line += 1

            for line in cost.cost_lines:
                value_split = 0.0
                for valuation in cost.valuation_adjustment_lines:
                    value = 0.0
                    if valuation.cost_line_id and valuation.cost_line_id.id == line.id:
                        if line.split_method == 'by_quantity' and total_qty:
                            per_unit = (line.price_unit / total_qty)
                            value = valuation.quantity * per_unit
                        elif line.split_method == 'by_weight' and total_weight:
                            per_unit = (line.price_unit / total_weight)
                            value = valuation.weight * per_unit
                        elif line.split_method == 'by_volume' and total_volume:
                            per_unit = (line.price_unit / total_volume)
                            value = valuation.volume * per_unit
                        elif line.split_method == 'equal':
                            value = (line.price_unit / total_line)
                        elif line.split_method == 'by_current_cost_price' and total_cost:
                            per_unit = (line.price_unit / total_cost)
                            value = valuation.former_cost * per_unit
                        else:
                            value = (line.price_unit / total_line)

                        if digits:
                            value = float_round(value, precision_digits=digits[1], rounding_method='UP')
                            fnc = min if line.price_unit > 0 else max
                            value = fnc(value, line.price_unit - value_split)
                            value_split += value

                        if valuation.id not in towrite_dict:
                            towrite_dict[valuation.id] = value
                        else:
                            towrite_dict[valuation.id] += value
        if towrite_dict:
            for key, value in towrite_dict.items():
                line_obj.write(cr, uid, key, {'additional_landed_cost': value}, context=context)
        return True
Esempio n. 28
0
class mrp_product_produce(osv.osv_memory):
    _name = "mrp.product.produce"
    _description = "Product Produce"

    _columns = {
        'product_id':
        fields.many2one('product.product', type='many2one'),
        'product_qty':
        fields.float(
            'Select Quantity',
            digits_compute=dp.get_precision('Product Unit of Measure'),
            required=True),
        'mode':
        fields.selection(
            [('consume_produce', 'Consume & Produce'),
             ('consume', 'Consume Only')],
            'Mode',
            required=True,
            help=
            "'Consume only' mode will only consume the products with the quantity selected.\n"
            "'Consume & Produce' mode will consume as well as produce the products with the quantity selected "
            "and it will finish the production order when total ordered quantities are produced."
        ),
        'lot_id':
        fields.many2one(
            'stock.production.lot', 'Lot'
        ),  #Should only be visible when it is consume and produce mode
        'consume_lines':
        fields.one2many('mrp.product.produce.line', 'produce_id',
                        'Products Consumed'),
        'tracking':
        fields.related('product_id',
                       'tracking',
                       type='selection',
                       selection=[('serial', 'By Unique Serial Number'),
                                  ('lot', 'By Lots'),
                                  ('none', 'No Tracking')]),
    }

    def on_change_qty(self,
                      cr,
                      uid,
                      ids,
                      product_qty,
                      consume_lines,
                      context=None):
        """ 
            When changing the quantity of products to be produced it will 
            recalculate the number of raw materials needed according
            to the scheduled products and the already consumed/produced products
            It will return the consume lines needed for the products to be produced
            which the user can still adapt
        """
        prod_obj = self.pool.get("mrp.production")
        uom_obj = self.pool.get("product.uom")
        production = prod_obj.browse(cr,
                                     uid,
                                     context['active_id'],
                                     context=context)
        consume_lines = []
        new_consume_lines = []
        if product_qty > 0.0:
            product_uom_qty = uom_obj._compute_qty(
                cr, uid, production.product_uom.id, product_qty,
                production.product_id.uom_id.id)
            consume_lines = prod_obj._calculate_qty(
                cr,
                uid,
                production,
                product_qty=product_uom_qty,
                context=context)

        for consume in consume_lines:
            new_consume_lines.append([0, False, consume])
        return {'value': {'consume_lines': new_consume_lines}}

    def _get_product_qty(self, cr, uid, context=None):
        """ To obtain product quantity
        @param self: The object pointer.
        @param cr: A database cursor
        @param uid: ID of the user currently logged in
        @param context: A standard dictionary
        @return: Quantity
        """
        if context is None:
            context = {}
        prod = self.pool.get('mrp.production').browse(cr,
                                                      uid,
                                                      context['active_id'],
                                                      context=context)
        done = 0.0
        for move in prod.move_created_ids2:
            if move.product_id == prod.product_id:
                if not move.scrapped:
                    done += move.product_uom_qty  # As uom of produced products and production order should correspond
        return prod.product_qty - done

    def _get_product_id(self, cr, uid, context=None):
        """ To obtain product id
        @return: id
        """
        prod = False
        if context and context.get("active_id"):
            prod = self.pool.get('mrp.production').browse(cr,
                                                          uid,
                                                          context['active_id'],
                                                          context=context)
        return prod and prod.product_id.id or False

    def _get_track(self, cr, uid, context=None):
        prod = self._get_product_id(cr, uid, context=context)
        prod_obj = self.pool.get("product.product")
        return prod and prod_obj.browse(cr, uid, prod,
                                        context=context).tracking or 'none'

    _defaults = {
        'product_qty': _get_product_qty,
        'mode': lambda *x: 'consume_produce',
        'product_id': _get_product_id,
        'tracking': _get_track,
    }

    def do_produce(self, cr, uid, ids, context=None):
        production_id = context.get('active_id', False)
        assert production_id, "Production Id should be specified in context as a Active ID."
        data = self.browse(cr, uid, ids[0], context=context)
        self.pool.get('mrp.production').action_produce(cr,
                                                       uid,
                                                       production_id,
                                                       data.product_qty,
                                                       data.mode,
                                                       data,
                                                       context=context)
        return {}
Esempio n. 29
0
class AccountInvoiceLine(models.Model):
    _inherit = 'account.invoice.line'

    asset_category_id = fields.Many2one('account.asset.category',
                                        string='Asset Category')
    asset_start_date = fields.Date(string='Asset End Date',
                                   compute='_get_asset_date',
                                   readonly=True,
                                   store=True)
    asset_end_date = fields.Date(string='Asset Start Date',
                                 compute='_get_asset_date',
                                 readonly=True,
                                 store=True)
    asset_mrr = fields.Float(string='Monthly Recurring Revenue',
                             compute='_get_asset_date',
                             readonly=True,
                             digits=dp.get_precision('Account'),
                             store=True)

    @api.one
    @api.depends('asset_category_id', 'invoice_id.date_invoice')
    def _get_asset_date(self):
        self.asset_mrr = 0
        self.asset_start_date = False
        self.asset_end_date = False
        cat = self.asset_category_id
        if cat:
            months = cat.method_number * cat.method_period
            if self.invoice_id.type in ['out_invoice', 'out_refund']:
                self.asset_mrr = self.price_subtotal_signed / months
            if self.invoice_id.date_invoice:
                start_date = datetime.strptime(self.invoice_id.date_invoice,
                                               DF).replace(day=1)
                end_date = (start_date + relativedelta(months=months, days=-1))
                self.asset_start_date = start_date.strftime(DF)
                self.asset_end_date = end_date.strftime(DF)

    @api.one
    def asset_create(self):
        if self.asset_category_id and self.asset_category_id.method_number > 1:
            vals = {
                'name': self.name,
                'code': self.invoice_id.number or False,
                'category_id': self.asset_category_id.id,
                'value': self.price_subtotal,
                'partner_id': self.invoice_id.partner_id.id,
                'company_id': self.invoice_id.company_id.id,
                'currency_id': self.invoice_id.currency_id.id,
                'date': self.asset_start_date or self.invoice_id.date_invoice,
                'invoice_id': self.invoice_id.id,
            }
            changed_vals = self.env[
                'account.asset.asset'].onchange_category_id_values(
                    vals['category_id'])
            vals.update(changed_vals['value'])
            asset = self.env['account.asset.asset'].create(vals)
            if self.asset_category_id.open_asset:
                asset.validate()
        return True

    @api.onchange('product_id')
    def onchange_product_id(self):
        if self.product_id:
            if self.invoice_id.type == 'out_invoice':
                self.asset_category_id = self.product_id.product_tmpl_id.deferred_revenue_category_id
            elif self.invoice_id.type == 'in_invoice':
                self.asset_category_id = self.product_id.product_tmpl_id.asset_category_id
Esempio n. 30
0
class mrp_repair_line(osv.osv, ProductChangeMixin):
    _name = 'mrp.repair.line'
    _description = 'Repair Line'

    def _amount_line(self, cr, uid, ids, field_name, arg, context=None):
        """ Calculates amount.
        @param field_name: Name of field.
        @param arg: Argument
        @return: Dictionary of values.
        """
        res = {}
        tax_obj = self.pool.get('account.tax')
        # cur_obj = self.pool.get('res.currency')
        for line in self.browse(cr, uid, ids, context=context):
            if line.to_invoice:
                cur = line.repair_id.pricelist_id.currency_id
                taxes = tax_obj.compute_all(cr, uid, line.tax_id, line.price_unit, cur.id, line.product_uom_qty, line.product_id.id, line.repair_id.partner_id.id)
                #res[line.id] = cur_obj.round(cr, uid, cur, taxes['total'])
                res[line.id] = taxes['total_included']
            else:
                res[line.id] = 0
        return res

    _columns = {
        'name': fields.char('Description', required=True),
        'repair_id': fields.many2one('mrp.repair', 'Repair Order Reference', ondelete='cascade', select=True),
        'type': fields.selection([('add', 'Add'), ('remove', 'Remove')], 'Type', required=True),
        'to_invoice': fields.boolean('To Invoice'),
        'product_id': fields.many2one('product.product', 'Product', required=True),
        'invoiced': fields.boolean('Invoiced', readonly=True, copy=False),
        'price_unit': fields.float('Unit Price', required=True, digits_compute=dp.get_precision('Product Price')),
        'price_subtotal': fields.function(_amount_line, string='Subtotal', digits=0),
        'tax_id': fields.many2many('account.tax', 'repair_operation_line_tax', 'repair_operation_line_id', 'tax_id', 'Taxes'),
        'product_uom_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
        'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True),
        'invoice_line_id': fields.many2one('account.invoice.line', 'Invoice Line', readonly=True, copy=False),
        'location_id': fields.many2one('stock.location', 'Source Location', required=True, select=True),
        'location_dest_id': fields.many2one('stock.location', 'Dest. Location', required=True, select=True),
        'move_id': fields.many2one('stock.move', 'Inventory Move', readonly=True, copy=False),
        'lot_id': fields.many2one('stock.production.lot', 'Lot'),
        'state': fields.selection([
                    ('draft', 'Draft'),
                    ('confirmed', 'Confirmed'),
                    ('done', 'Done'),
                    ('cancel', 'Cancelled')], 'Status', required=True, readonly=True, copy=False,
                    help=' * The \'Draft\' status is set automatically as draft when repair order in draft status. \
                        \n* The \'Confirmed\' status is set automatically as confirm when repair order in confirm status. \
                        \n* The \'Done\' status is set automatically when repair order is completed.\
                        \n* The \'Cancelled\' status is set automatically when user cancel repair order.'),
    }
    _defaults = {
        'state': lambda *a: 'draft',
        'product_uom_qty': lambda *a: 1,
    }

    def onchange_operation_type(self, cr, uid, ids, type, guarantee_limit, company_id=False, context=None):
        """ On change of operation type it sets source location, destination location
        and to invoice field.
        @param product: Changed operation type.
        @param guarantee_limit: Guarantee limit of current record.
        @return: Dictionary of values.
        """
        if not type:
            return {'value': {
                'location_id': False,
                'location_dest_id': False
                }}
        location_obj = self.pool.get('stock.location')
        warehouse_obj = self.pool.get('stock.warehouse')
        location_id = location_obj.search(cr, uid, [('usage', '=', 'production')], context=context)
        location_id = location_id and location_id[0] or False

        if type == 'add':
            # TOCHECK: Find stock location for user's company warehouse or
            # repair order's company's warehouse (company_id field is added in fix of lp:831583)
            args = company_id and [('company_id', '=', company_id)] or []
            warehouse_ids = warehouse_obj.search(cr, uid, args, context=context)
            stock_id = False
            if warehouse_ids:
                stock_id = warehouse_obj.browse(cr, uid, warehouse_ids[0], context=context).lot_stock_id.id
            to_invoice = (guarantee_limit and datetime.strptime(guarantee_limit, '%Y-%m-%d') < datetime.now())

            return {'value': {
                'to_invoice': to_invoice,
                'location_id': stock_id,
                'location_dest_id': location_id
                }}
        scrap_location_ids = location_obj.search(cr, uid, [('scrap_location', '=', True)], context=context)

        return {'value': {
                'to_invoice': False,
                'location_id': location_id,
                'location_dest_id': scrap_location_ids and scrap_location_ids[0] or False,
                }}
Esempio n. 31
0
class mrp_repair(osv.osv):
    _name = 'mrp.repair'
    _inherit = 'mail.thread'
    _description = 'Repair Order'

    def _amount_untaxed(self, cr, uid, ids, field_name, arg, context=None):
        """ Calculates untaxed amount.
        @param self: The object pointer
        @param cr: The current row, from the database cursor,
        @param uid: The current user ID for security checks
        @param ids: List of selected IDs
        @param field_name: Name of field.
        @param arg: Argument
        @param context: A standard dictionary for contextual values
        @return: Dictionary of values.
        """
        res = {}
        cur_obj = self.pool.get('res.currency')

        for repair in self.browse(cr, uid, ids, context=context):
            res[repair.id] = 0.0
            for line in repair.operations:
                res[repair.id] += line.price_subtotal
            for line in repair.fees_lines:
                res[repair.id] += line.price_subtotal
            cur = repair.pricelist_id.currency_id
            res[repair.id] = cur_obj.round(cr, uid, cur, res[repair.id])
        return res

    def _amount_tax(self, cr, uid, ids, field_name, arg, context=None):
        """ Calculates taxed amount.
        @param field_name: Name of field.
        @param arg: Argument
        @return: Dictionary of values.
        """
        res = {}
        #return {}.fromkeys(ids, 0)
        cur_obj = self.pool.get('res.currency')
        tax_obj = self.pool.get('account.tax')
        for repair in self.browse(cr, uid, ids, context=context):
            val = 0.0
            cur = repair.pricelist_id.currency_id
            for line in repair.operations:
                #manage prices with tax included use compute_all instead of compute
                if line.to_invoice and line.tax_id:
                    tax_calculate = tax_obj.compute_all(cr, uid, line.tax_id, line.price_unit, cur, line.product_uom_qty, line.product_id.id, repair.partner_id.id)
                    for c in tax_calculate['taxes']:
                        val += c['amount']
            for line in repair.fees_lines:
                if line.to_invoice and line.tax_id:
                    tax_calculate = tax_obj.compute_all(cr, uid, line.tax_id, line.price_unit, cur, line.product_uom_qty, line.product_id.id, repair.partner_id.id)
                    for c in tax_calculate['taxes']:
                        val += c['amount']
            res[repair.id] = cur_obj.round(cr, uid, cur, val)
        return res

    def _amount_total(self, cr, uid, ids, field_name, arg, context=None):
        """ Calculates total amount.
        @param field_name: Name of field.
        @param arg: Argument
        @return: Dictionary of values.
        """
        res = {}
        untax = self._amount_untaxed(cr, uid, ids, field_name, arg, context=context)
        tax = self._amount_tax(cr, uid, ids, field_name, arg, context=context)
        cur_obj = self.pool.get('res.currency')
        for id in ids:
            repair = self.browse(cr, uid, id, context=context)
            cur = repair.pricelist_id.currency_id
            res[id] = cur_obj.round(cr, uid, cur, untax.get(id, 0.0) + tax.get(id, 0.0))
        return res

    def _get_default_address(self, cr, uid, ids, field_name, arg, context=None):
        res = {}
        partner_obj = self.pool.get('res.partner')
        for data in self.browse(cr, uid, ids, context=context):
            adr_id = False
            if data.partner_id:
                adr_id = partner_obj.address_get(cr, uid, [data.partner_id.id], ['contact'])['contact']
            res[data.id] = adr_id
        return res

    def _get_lines(self, cr, uid, ids, context=None):
        return self.pool['mrp.repair'].search(cr, uid, [('operations', 'in', ids)], context=context)

    def _get_fee_lines(self, cr, uid, ids, context=None):
        return self.pool['mrp.repair'].search(cr, uid, [('fees_lines', 'in', ids)], context=context)

    _columns = {
        'name': fields.char('Repair Reference', required=True, states={'confirmed': [('readonly', True)]}, copy=False),
        'product_id': fields.many2one('product.product', string='Product to Repair', required=True, readonly=True, states={'draft': [('readonly', False)]}),
        'product_qty': fields.float('Product Quantity', digits_compute=dp.get_precision('Product Unit of Measure'),
                                    required=True, readonly=True, states={'draft': [('readonly', False)]}),
        'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, readonly=True, states={'draft': [('readonly', False)]}),
        'partner_id': fields.many2one('res.partner', 'Partner', select=True, help='Choose partner for whom the order will be invoiced and delivered.', states={'confirmed': [('readonly', True)]}),
        'address_id': fields.many2one('res.partner', 'Delivery Address', domain="[('parent_id','=',partner_id)]", states={'confirmed': [('readonly', True)]}),
        'default_address_id': fields.function(_get_default_address, type="many2one", relation="res.partner"),
        'state': fields.selection([
            ('draft', 'Quotation'),
            ('cancel', 'Cancelled'),
            ('confirmed', 'Confirmed'),
            ('under_repair', 'Under Repair'),
            ('ready', 'Ready to Repair'),
            ('2binvoiced', 'To be Invoiced'),
            ('invoice_except', 'Invoice Exception'),
            ('done', 'Repaired')
            ], 'Status', readonly=True, track_visibility='onchange', copy=False,
            help=' * The \'Draft\' status is used when a user is encoding a new and unconfirmed repair order. \
            \n* The \'Confirmed\' status is used when a user confirms the repair order. \
            \n* The \'Ready to Repair\' status is used to start to repairing, user can start repairing only after repair order is confirmed. \
            \n* The \'To be Invoiced\' status is used to generate the invoice before or after repairing done. \
            \n* The \'Done\' status is set when repairing is completed.\
            \n* The \'Cancelled\' status is used when user cancel repair order.'),
        'location_id': fields.many2one('stock.location', 'Current Location', select=True, required=True, readonly=True, states={'draft': [('readonly', False)], 'confirmed': [('readonly', True)]}),
        'location_dest_id': fields.many2one('stock.location', 'Delivery Location', readonly=True, required=True, states={'draft': [('readonly', False)], 'confirmed': [('readonly', True)]}),
        'lot_id': fields.many2one('stock.production.lot', 'Repaired Lot', domain="[('product_id','=', product_id)]", help="Products repaired are all belonging to this lot", oldname="prodlot_id"),
        'guarantee_limit': fields.date('Warranty Expiration', states={'confirmed': [('readonly', True)]}),
        'operations': fields.one2many('mrp.repair.line', 'repair_id', 'Operation Lines', readonly=True, states={'draft': [('readonly', False)]}, copy=True),
        'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', help='Pricelist of the selected partner.'),
        'partner_invoice_id': fields.many2one('res.partner', 'Invoicing Address'),
        'invoice_method': fields.selection([
            ("none", "No Invoice"),
            ("b4repair", "Before Repair"),
            ("after_repair", "After Repair")
           ], "Invoice Method",
            select=True, required=True, states={'draft': [('readonly', False)]}, readonly=True, help='Selecting \'Before Repair\' or \'After Repair\' will allow you to generate invoice before or after the repair is done respectively. \'No invoice\' means you don\'t want to generate invoice for this repair order.'),
        'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True, track_visibility="onchange", copy=False),
        'move_id': fields.many2one('stock.move', 'Move', readonly=True, help="Move created by the repair order", track_visibility="onchange", copy=False),
        'fees_lines': fields.one2many('mrp.repair.fee', 'repair_id', 'Fees', readonly=True, states={'draft': [('readonly', False)]}, copy=True),
        'internal_notes': fields.text('Internal Notes'),
        'quotation_notes': fields.text('Quotation Notes'),
        'company_id': fields.many2one('res.company', 'Company'),
        'invoiced': fields.boolean('Invoiced', readonly=True, copy=False),
        'repaired': fields.boolean('Repaired', readonly=True, copy=False),
        'amount_untaxed': fields.function(_amount_untaxed, string='Untaxed Amount',
            store={
                'mrp.repair': (lambda self, cr, uid, ids, c={}: ids, ['operations', 'fees_lines'], 10),
                'mrp.repair.line': (_get_lines, ['price_unit', 'price_subtotal', 'product_id', 'tax_id', 'product_uom_qty', 'product_uom'], 10),
                'mrp.repair.fee': (_get_fee_lines, ['price_unit', 'price_subtotal', 'product_id', 'tax_id', 'product_uom_qty', 'product_uom'], 10),
            }),
        'amount_tax': fields.function(_amount_tax, string='Taxes',
            store={
                'mrp.repair': (lambda self, cr, uid, ids, c={}: ids, ['operations', 'fees_lines'], 10),
                'mrp.repair.line': (_get_lines, ['price_unit', 'price_subtotal', 'product_id', 'tax_id', 'product_uom_qty', 'product_uom'], 10),
                'mrp.repair.fee': (_get_fee_lines, ['price_unit', 'price_subtotal', 'product_id', 'tax_id', 'product_uom_qty', 'product_uom'], 10),
            }),
        'amount_total': fields.function(_amount_total, string='Total',
            store={
                'mrp.repair': (lambda self, cr, uid, ids, c={}: ids, ['operations', 'fees_lines'], 10),
                'mrp.repair.line': (_get_lines, ['price_unit', 'price_subtotal', 'product_id', 'tax_id', 'product_uom_qty', 'product_uom'], 10),
                'mrp.repair.fee': (_get_fee_lines, ['price_unit', 'price_subtotal', 'product_id', 'tax_id', 'product_uom_qty', 'product_uom'], 10),
            }),
    }

    def _default_stock_location(self, cr, uid, context=None):
        try:
            warehouse = self.pool.get('ir.model.data').get_object(cr, uid, 'stock', 'warehouse0')
            return warehouse.lot_stock_id.id
        except:
            return False

    _defaults = {
        'state': lambda *a: 'draft',
        'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').next_by_code(cr, uid, 'mrp.repair'),
        'invoice_method': lambda *a: 'none',
        'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.repair', context=context),
        'pricelist_id': lambda self, cr, uid, context: self.pool['product.pricelist'].search(cr, uid, [], limit=1)[0],
        'product_qty': 1.0,
        'location_id': _default_stock_location,
    }

    _sql_constraints = [
        ('name', 'unique (name)', 'The name of the Repair Order must be unique!'),
    ]

    def onchange_product_id(self, cr, uid, ids, product_id=None):
        """ On change of product sets some values.
        @param product_id: Changed product
        @return: Dictionary of values.
        """
        product = False
        if product_id:
            product = self.pool.get("product.product").browse(cr, uid, product_id)
        return {'value': {
                    'guarantee_limit': False,
                    'lot_id': False,
                    'product_uom': product and product.uom_id.id or False,
                }
        }

    def onchange_product_uom(self, cr, uid, ids, product_id, product_uom, context=None):
        res = {'value': {}}
        if not product_uom or not product_id:
            return res
        product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
        uom = self.pool.get('product.uom').browse(cr, uid, product_uom, context=context)
        if uom.category_id.id != product.uom_id.category_id.id:
            res['warning'] = {'title': _('Warning'), 'message': _('The Product Unit of Measure you chose has a different category than in the product form.')}
            res['value'].update({'product_uom': product.uom_id.id})
        return res

    def onchange_location_id(self, cr, uid, ids, location_id=None):
        """ On change of location
        """
        return {'value': {'location_dest_id': location_id}}

    def button_dummy(self, cr, uid, ids, context=None):
        return True

    def onchange_partner_id(self, cr, uid, ids, part, address_id):
        """ On change of partner sets the values of partner address,
        partner invoice address and pricelist.
        @param part: Changed id of partner.
        @param address_id: Address id from current record.
        @return: Dictionary of values.
        """
        part_obj = self.pool.get('res.partner')
        pricelist_obj = self.pool.get('product.pricelist')
        if not part:
            return {'value': {
                        'address_id': False,
                        'partner_invoice_id': False,
                        'pricelist_id': pricelist_obj.search(cr, uid, [], limit=1)[0]
                    }
            }
        addr = part_obj.address_get(cr, uid, [part], ['delivery', 'invoice', 'contact'])
        partner = part_obj.browse(cr, uid, part)
        pricelist = partner.property_product_pricelist and partner.property_product_pricelist.id or False
        return {'value': {
                    'address_id': addr['delivery'] or addr['contact'],
                    'partner_invoice_id': addr['invoice'],
                    'pricelist_id': pricelist
                }
        }

    def action_cancel_draft(self, cr, uid, ids, *args):
        """ Cancels repair order when it is in 'Draft' state.
        @param *arg: Arguments
        @return: True
        """
        if not len(ids):
            return False
        mrp_line_obj = self.pool.get('mrp.repair.line')
        for repair in self.browse(cr, uid, ids):
            mrp_line_obj.write(cr, uid, [l.id for l in repair.operations], {'state': 'draft'})
        self.write(cr, uid, ids, {'state': 'draft'})
        return self.create_workflow(cr, uid, ids)

    def action_confirm(self, cr, uid, ids, *args):
        """ Repair order state is set to 'To be invoiced' when invoice method
        is 'Before repair' else state becomes 'Confirmed'.
        @param *arg: Arguments
        @return: True
        """
        mrp_line_obj = self.pool.get('mrp.repair.line')
        for o in self.browse(cr, uid, ids):
            if (o.invoice_method == 'b4repair'):
                self.write(cr, uid, [o.id], {'state': '2binvoiced'})
            else:
                self.write(cr, uid, [o.id], {'state': 'confirmed'})
                for line in o.operations:
                    if line.product_id.tracking != 'none' and not line.lot_id:
                        raise UserError(_("Serial number is required for operation line with product '%s'") % (line.product_id.name))
                mrp_line_obj.write(cr, uid, [l.id for l in o.operations], {'state': 'confirmed'})
        return True

    def action_cancel(self, cr, uid, ids, context=None):
        """ Cancels repair order.
        @return: True
        """
        mrp_line_obj = self.pool.get('mrp.repair.line')
        for repair in self.browse(cr, uid, ids, context=context):
            if not repair.invoiced:
                mrp_line_obj.write(cr, uid, [l.id for l in repair.operations], {'state': 'cancel'}, context=context)
            else:
                raise UserError(_('Repair order is already invoiced.'))
        return self.write(cr, uid, ids, {'state': 'cancel'})

    def wkf_invoice_create(self, cr, uid, ids, *args):
        self.action_invoice_create(cr, uid, ids)
        return True

    def action_invoice_create(self, cr, uid, ids, group=False, context=None):
        """ Creates invoice(s) for repair order.
        @param group: It is set to true when group invoice is to be generated.
        @return: Invoice Ids.
        """
        res = {}
        invoices_group = {}
        inv_line_obj = self.pool.get('account.invoice.line')
        inv_obj = self.pool.get('account.invoice')
        repair_line_obj = self.pool.get('mrp.repair.line')
        repair_fee_obj = self.pool.get('mrp.repair.fee')
        for repair in self.browse(cr, uid, ids, context=context):
            res[repair.id] = False
            if repair.state in ('draft', 'cancel') or repair.invoice_id:
                continue
            if not (repair.partner_id.id and repair.partner_invoice_id.id):
                raise UserError(_('You have to select a Partner Invoice Address in the repair form!'))
            comment = repair.quotation_notes
            if (repair.invoice_method != 'none'):
                if group and repair.partner_invoice_id.id in invoices_group:
                    inv_id = invoices_group[repair.partner_invoice_id.id]
                    invoice = inv_obj.browse(cr, uid, inv_id)
                    invoice_vals = {
                        'name': invoice.name + ', ' + repair.name,
                        'origin': invoice.origin + ', ' + repair.name,
                        'comment': (comment and (invoice.comment and invoice.comment + "\n" + comment or comment)) or (invoice.comment and invoice.comment or ''),
                    }
                    inv_obj.write(cr, uid, [inv_id], invoice_vals, context=context)
                else:
                    if not repair.partner_id.property_account_receivable_id:
                        raise UserError(_('No account defined for partner "%s".') % repair.partner_id.name)
                    account_id = repair.partner_id.property_account_receivable_id.id
                    inv = {
                        'name': repair.name,
                        'origin': repair.name,
                        'type': 'out_invoice',
                        'account_id': account_id,
                        'partner_id': repair.partner_invoice_id.id or repair.partner_id.id,
                        'currency_id': repair.pricelist_id.currency_id.id,
                        'comment': repair.quotation_notes,
                        'fiscal_position_id': repair.partner_id.property_account_position_id.id
                    }
                    inv_id = inv_obj.create(cr, uid, inv)
                    invoices_group[repair.partner_invoice_id.id] = inv_id
                self.write(cr, uid, repair.id, {'invoiced': True, 'invoice_id': inv_id})

                for operation in repair.operations:
                    if operation.to_invoice:
                        if group:
                            name = repair.name + '-' + operation.name
                        else:
                            name = operation.name

                        if operation.product_id.property_account_income_id:
                            account_id = operation.product_id.property_account_income_id.id
                        elif operation.product_id.categ_id.property_account_income_categ_id:
                            account_id = operation.product_id.categ_id.property_account_income_categ_id.id
                        else:
                            raise UserError(_('No account defined for product "%s".') % operation.product_id.name)

                        invoice_line_id = inv_line_obj.create(cr, uid, {
                            'invoice_id': inv_id,
                            'name': name,
                            'origin': repair.name,
                            'account_id': account_id,
                            'quantity': operation.product_uom_qty,
                            'invoice_line_tax_ids': [(6, 0, [x.id for x in operation.tax_id])],
                            'uom_id': operation.product_uom.id,
                            'price_unit': operation.price_unit,
                            'price_subtotal': operation.product_uom_qty * operation.price_unit,
                            'product_id': operation.product_id and operation.product_id.id or False
                        })
                        repair_line_obj.write(cr, uid, [operation.id], {'invoiced': True, 'invoice_line_id': invoice_line_id})
                for fee in repair.fees_lines:
                    if fee.to_invoice:
                        if group:
                            name = repair.name + '-' + fee.name
                        else:
                            name = fee.name
                        if not fee.product_id:
                            raise UserError(_('No product defined on Fees!'))

                        if fee.product_id.property_account_income_id:
                            account_id = fee.product_id.property_account_income_id.id
                        elif fee.product_id.categ_id.property_account_income_categ_id:
                            account_id = fee.product_id.categ_id.property_account_income_categ_id.id
                        else:
                            raise UserError(_('No account defined for product "%s".') % fee.product_id.name)

                        invoice_fee_id = inv_line_obj.create(cr, uid, {
                            'invoice_id': inv_id,
                            'name': name,
                            'origin': repair.name,
                            'account_id': account_id,
                            'quantity': fee.product_uom_qty,
                            'invoice_line_tax_ids': [(6, 0, [x.id for x in fee.tax_id])],
                            'uom_id': fee.product_uom.id,
                            'product_id': fee.product_id and fee.product_id.id or False,
                            'price_unit': fee.price_unit,
                            'price_subtotal': fee.product_uom_qty * fee.price_unit
                        })
                        repair_fee_obj.write(cr, uid, [fee.id], {'invoiced': True, 'invoice_line_id': invoice_fee_id})
                #inv_obj.button_reset_taxes(cr, uid, inv_id, context=context)
                res[repair.id] = inv_id
        return res

    def action_repair_ready(self, cr, uid, ids, context=None):
        """ Writes repair order state to 'Ready'
        @return: True
        """
        for repair in self.browse(cr, uid, ids, context=context):
            self.pool.get('mrp.repair.line').write(cr, uid, [l.id for
                    l in repair.operations], {'state': 'confirmed'}, context=context)
            self.write(cr, uid, [repair.id], {'state': 'ready'})
        return True

    def action_repair_start(self, cr, uid, ids, context=None):
        """ Writes repair order state to 'Under Repair'
        @return: True
        """
        repair_line = self.pool.get('mrp.repair.line')
        for repair in self.browse(cr, uid, ids, context=context):
            repair_line.write(cr, uid, [l.id for
                    l in repair.operations], {'state': 'confirmed'}, context=context)
            repair.write({'state': 'under_repair'})
        return True

    def action_repair_end(self, cr, uid, ids, context=None):
        """ Writes repair order state to 'To be invoiced' if invoice method is
        After repair else state is set to 'Ready'.
        @return: True
        """
        for order in self.browse(cr, uid, ids, context=context):
            val = {}
            val['repaired'] = True
            if (not order.invoiced and order.invoice_method == 'after_repair'):
                val['state'] = '2binvoiced'
            elif (not order.invoiced and order.invoice_method == 'b4repair'):
                val['state'] = 'ready'
            else:
                pass
            self.write(cr, uid, [order.id], val)
        return True

    def wkf_repair_done(self, cr, uid, ids, *args):
        self.action_repair_done(cr, uid, ids)
        return True

    def action_repair_done(self, cr, uid, ids, context=None):
        """ Creates stock move for operation and stock move for final product of repair order.
        @return: Move ids of final products
        """
        res = {}
        move_obj = self.pool.get('stock.move')
        repair_line_obj = self.pool.get('mrp.repair.line')
        for repair in self.browse(cr, uid, ids, context=context):
            move_ids = []
            for move in repair.operations:
                move_id = move_obj.create(cr, uid, {
                    'name': move.name,
                    'product_id': move.product_id.id,
                    'restrict_lot_id': move.lot_id.id,
                    'product_uom_qty': move.product_uom_qty,
                    'product_uom': move.product_uom.id,
                    'partner_id': repair.address_id and repair.address_id.id or False,
                    'location_id': move.location_id.id,
                    'location_dest_id': move.location_dest_id.id,
                })
                move_ids.append(move_id)
                repair_line_obj.write(cr, uid, [move.id], {'move_id': move_id, 'state': 'done'}, context=context)
            move_id = move_obj.create(cr, uid, {
                'name': repair.name,
                'product_id': repair.product_id.id,
                'product_uom': repair.product_uom.id or repair.product_id.uom_id.id,
                'product_uom_qty': repair.product_qty,
                'partner_id': repair.address_id and repair.address_id.id or False,
                'location_id': repair.location_id.id,
                'location_dest_id': repair.location_dest_id.id,
                'restrict_lot_id': repair.lot_id.id,
            })
            move_ids.append(move_id)
            move_obj.action_done(cr, uid, move_ids, context=context)
            self.write(cr, uid, [repair.id], {'state': 'done', 'move_id': move_id}, context=context)
            res[repair.id] = move_id
        return res