Example #1
0
class AccountVoucher(models.Model):
    @api.one
    @api.depends('move_id.line_ids.reconciled',
                 'move_id.line_ids.account_id.internal_type')
    def _check_paid(self):
        self.paid = any([((line.account_id.internal_type, 'in',
                           ('receivable', 'payable')) and line.reconciled)
                         for line in self.move_id.line_ids])

    @api.model
    def _get_currency(self):
        journal = self.env['account.journal'].browse(
            self._context.get('journal_id', False))
        if journal.currency_id:
            return journal.currency_id.id
        return self.env.user.company_id.currency_id.id

    @api.model
    def _get_company(self):
        return self._context.get('company_id', self.env.user.company_id.id)

    @api.multi
    @api.depends('name', 'number')
    def name_get(self):
        return [(r.id, (r.number or _('Voucher'))) for r in self]

    @api.one
    @api.depends('journal_id', 'company_id')
    def _get_journal_currency(self):
        self.currency_id = self.journal_id.currency_id.id or self.company_id.currency_id.id

    @api.model
    def _default_journal(self):
        voucher_type = self._context.get('voucher_type', 'sale')
        company_id = self._context.get('company_id',
                                       self.env.user.company_id.id)
        domain = [
            ('type', '=', voucher_type),
            ('company_id', '=', company_id),
        ]
        return self.env['account.journal'].search(domain, limit=1)

    @api.multi
    @api.depends('tax_correction', 'line_ids.price_subtotal')
    def _compute_total(self):
        for voucher in self:
            total = 0
            tax_amount = 0
            for line in voucher.line_ids:
                tax_info = line.tax_ids.compute_all(line.price_unit,
                                                    voucher.currency_id,
                                                    line.quantity,
                                                    line.product_id,
                                                    voucher.partner_id)
                total += tax_info.get('total_included', 0.0)
                tax_amount += sum([
                    t.get('amount', 0.0) for t in tax_info.get('taxes', False)
                ])
            voucher.amount = total + voucher.tax_correction
            voucher.tax_amount = tax_amount

    @api.one
    @api.depends('account_pay_now_id', 'account_pay_later_id', 'pay_now')
    def _get_account(self):
        self.account_id = self.account_pay_now_id if self.pay_now == 'pay_now' else self.account_pay_later_id

    _name = 'account.voucher'
    _description = 'Accounting Voucher'
    _inherit = ['mail.thread']
    _order = "date desc, id desc"

    voucher_type = fields.Selection([('sale', 'Sale'),
                                     ('purchase', 'Purchase')],
                                    string='Type',
                                    readonly=True,
                                    states={'draft': [('readonly', False)]},
                                    oldname="type")
    name = fields.Char('Payment Reference',
                       readonly=True,
                       states={'draft': [('readonly', False)]},
                       default='')
    date = fields.Date(readonly=True,
                       select=True,
                       states={'draft': [('readonly', False)]},
                       help="Effective date for accounting entries",
                       copy=False,
                       default=fields.Date.context_today)
    journal_id = fields.Many2one('account.journal',
                                 'Journal',
                                 required=True,
                                 readonly=True,
                                 states={'draft': [('readonly', False)]},
                                 default=_default_journal)
    account_id = fields.Many2one(
        'account.account',
        'Account',
        required=True,
        readonly=True,
        states={'draft': [('readonly', False)]},
        domain=
        "[('deprecated', '=', False), ('internal_type','=', (pay_now == 'pay_now' and 'liquidity' or 'receivable'))]"
    )
    line_ids = fields.One2many('account.voucher.line',
                               'voucher_id',
                               'Voucher Lines',
                               readonly=True,
                               copy=True,
                               states={'draft': [('readonly', False)]})
    narration = fields.Text('Notes',
                            readonly=True,
                            states={'draft': [('readonly', False)]})
    currency_id = fields.Many2one('res.currency',
                                  compute='_get_journal_currency',
                                  string='Currency',
                                  readonly=True,
                                  required=True,
                                  default=lambda self: self._get_currency())
    company_id = fields.Many2one('res.company',
                                 'Company',
                                 required=True,
                                 readonly=True,
                                 states={'draft': [('readonly', False)]},
                                 related='journal_id.company_id',
                                 default=lambda self: self._get_company())
    state = fields.Selection(
        [('draft', 'Draft'), ('cancel', 'Cancelled'),
         ('proforma', 'Pro-forma'), ('posted', 'Posted')],
        'Status',
        readonly=True,
        track_visibility='onchange',
        copy=False,
        default='draft',
        help=
        " * The 'Draft' status is used when a user is encoding a new and unconfirmed Voucher.\n"
        " * The 'Pro-forma' status is used when the voucher does not have a voucher number.\n"
        " * The 'Posted' status is used when user create voucher,a voucher number is generated and voucher entries are created in account.\n"
        " * The 'Cancelled' status is used when user cancel voucher.")
    reference = fields.Char('Bill Reference',
                            readonly=True,
                            states={'draft': [('readonly', False)]},
                            help="The partner reference of this document.",
                            copy=False)
    amount = fields.Monetary(string='Total',
                             store=True,
                             readonly=True,
                             compute='_compute_total')
    tax_amount = fields.Monetary(readonly=True,
                                 store=True,
                                 compute='_compute_total')
    tax_correction = fields.Monetary(
        readonly=True,
        states={'draft': [('readonly', False)]},
        help=
        'In case we have a rounding problem in the tax, use this field to correct it'
    )
    number = fields.Char(readonly=True, copy=False)
    move_id = fields.Many2one('account.move', 'Journal Entry', copy=False)
    partner_id = fields.Many2one('res.partner',
                                 'Partner',
                                 change_default=1,
                                 readonly=True,
                                 states={'draft': [('readonly', False)]})
    paid = fields.Boolean(compute='_check_paid',
                          help="The Voucher has been totally paid.")
    pay_now = fields.Selection([
        ('pay_now', 'Pay Directly'),
        ('pay_later', 'Pay Later'),
    ],
                               'Payment',
                               select=True,
                               readonly=True,
                               states={'draft': [('readonly', False)]},
                               default='pay_later')
    date_due = fields.Date('Due Date',
                           readonly=True,
                           select=True,
                           states={'draft': [('readonly', False)]})

    @api.onchange('partner_id', 'pay_now')
    def onchange_partner_id(self):
        if self.pay_now == 'pay_now':
            liq_journal = self.env['account.journal'].search(
                [('type', 'in', ('bank', 'cash'))], limit=1)
            self.account_id = liq_journal.default_debit_account_id \
                if self.voucher_type == 'sale' else liq_journal.default_credit_account_id
        else:
            if self.partner_id:
                self.account_id = self.partner_id.property_account_receivable_id \
                    if self.voucher_type == 'sale' else self.partner_id.property_account_payable_id
            else:
                self.account_id = self.journal_id.default_debit_account_id \
                    if self.voucher_type == 'sale' else self.journal_id.default_credit_account_id

    @api.multi
    def button_proforma_voucher(self):
        self.signal_workflow('proforma_voucher')
        return {'type': 'ir.actions.act_window_close'}

    @api.multi
    def proforma_voucher(self):
        self.action_move_line_create()

    @api.multi
    def action_cancel_draft(self):
        self.create_workflow()
        self.write({'state': 'draft'})

    @api.multi
    def cancel_voucher(self):
        for voucher in self:
            voucher.move_id.button_cancel()
            voucher.move_id.unlink()
        self.write({'state': 'cancel', 'move_id': False})

    @api.multi
    def unlink(self):
        for voucher in self:
            if voucher.state not in ('draft', 'cancel'):
                raise Warning(
                    _('Cannot delete voucher(s) which are already opened or paid.'
                      ))
        return super(AccountVoucher, self).unlink()

    @api.multi
    def first_move_line_get(self, move_id, company_currency, current_currency):
        debit = credit = 0.0
        if self.voucher_type == 'purchase':
            credit = self._convert_amount(self.amount)
        elif self.voucher_type == 'sale':
            debit = self._convert_amount(self.amount)
        if debit < 0.0: debit = 0.0
        if credit < 0.0: credit = 0.0
        sign = debit - credit < 0 and -1 or 1
        #set the first line of the voucher
        move_line = {
            'name':
            self.name or '/',
            'debit':
            debit,
            'credit':
            credit,
            'account_id':
            self.account_id.id,
            'move_id':
            move_id,
            'journal_id':
            self.journal_id.id,
            'partner_id':
            self.partner_id.id,
            'currency_id':
            company_currency != current_currency and current_currency or False,
            'amount_currency': (
                sign * abs(self.amount)  # amount < 0 for refunds
                if company_currency != current_currency else 0.0),
            'date':
            self.date,
            'date_maturity':
            self.date_due
        }
        return move_line

    @api.multi
    def account_move_get(self):
        if self.number:
            name = self.number
        elif self.journal_id.sequence_id:
            if not self.journal_id.sequence_id.active:
                raise UserError(
                    _('Please activate the sequence of selected journal !'))
            name = self.journal_id.sequence_id.with_context(
                ir_sequence_date=self.date).next_by_id()
        else:
            raise UserError(_('Please define a sequence on the journal.'))

        move = {
            'name': name,
            'journal_id': self.journal_id.id,
            'narration': self.narration,
            'date': self.date,
            'ref': self.reference,
        }
        return move

    @api.multi
    def _convert_amount(self, amount):
        '''
        This function convert the amount given in company currency. It takes either the rate in the voucher (if the
        payment_rate_currency_id is relevant) either the rate encoded in the system.
        :param amount: float. The amount to convert
        :param voucher: id of the voucher on which we want the conversion
        :param context: to context to use for the conversion. It may contain the key 'date' set to the voucher date
            field in order to select the good rate to use.
        :return: the amount in the currency of the voucher's company
        :rtype: float
        '''
        for voucher in self:
            return voucher.currency_id.compute(amount,
                                               voucher.company_id.currency_id)

    @api.multi
    def voucher_move_line_create(self, line_total, move_id, company_currency,
                                 current_currency):
        '''
        Create one account move line, on the given account move, per voucher line where amount is not 0.0.
        It returns Tuple with tot_line what is total of difference between debit and credit and
        a list of lists with ids to be reconciled with this format (total_deb_cred,list_of_lists).

        :param voucher_id: Voucher id what we are working with
        :param line_total: Amount of the first line, which correspond to the amount we should totally split among all voucher lines.
        :param move_id: Account move wher those lines will be joined.
        :param company_currency: id of currency of the company to which the voucher belong
        :param current_currency: id of currency of the voucher
        :return: Tuple build as (remaining amount not allocated on voucher lines, list of account_move_line created in this method)
        :rtype: tuple(float, list of int)
        '''
        for line in self.line_ids:
            #create one move line per voucher line where amount is not 0.0
            if not line.price_subtotal:
                continue
            # convert the amount set on the voucher line into the currency of the voucher's company
            # this calls res_curreny.compute() with the right context, so that it will take either the rate on the voucher if it is relevant or will use the default behaviour
            amount = self._convert_amount(line.price_unit * line.quantity)
            move_line = {
                'journal_id':
                self.journal_id.id,
                'name':
                line.name or '/',
                'account_id':
                line.account_id.id,
                'move_id':
                move_id,
                'partner_id':
                self.partner_id.id,
                'analytic_account_id':
                line.account_analytic_id and line.account_analytic_id.id
                or False,
                'quantity':
                1,
                'credit':
                abs(amount) if self.voucher_type == 'sale' else 0.0,
                'debit':
                abs(amount) if self.voucher_type == 'purchase' else 0.0,
                'date':
                self.date,
                'tax_ids': [(4, t.id) for t in line.tax_ids],
                'amount_currency':
                line.price_subtotal
                if current_currency != company_currency else 0.0,
            }

            self.env['account.move.line'].create(move_line)
        return line_total

    @api.multi
    def action_move_line_create(self):
        '''
        Confirm the vouchers given in ids and create the journal entries for each of them
        '''
        for voucher in self:
            local_context = dict(
                self._context, force_company=voucher.journal_id.company_id.id)
            if voucher.move_id:
                continue
            company_currency = voucher.journal_id.company_id.currency_id.id
            current_currency = voucher.currency_id.id or company_currency
            # we select the context to use accordingly if it's a multicurrency case or not
            # But for the operations made by _convert_amount, we always need to give the date in the context
            ctx = local_context.copy()
            ctx['date'] = voucher.date
            ctx['check_move_validity'] = False
            # Create the account move record.
            move = self.env['account.move'].create(voucher.account_move_get())
            # Get the name of the account_move just created
            # Create the first line of the voucher
            move_line = self.env['account.move.line'].with_context(ctx).create(
                voucher.first_move_line_get(move.id, company_currency,
                                            current_currency))
            line_total = move_line.debit - move_line.credit
            if voucher.voucher_type == 'sale':
                line_total = line_total - voucher._convert_amount(
                    voucher.tax_amount)
            elif voucher.voucher_type == 'purchase':
                line_total = line_total + voucher._convert_amount(
                    voucher.tax_amount)
            # Create one move line per voucher line where amount is not 0.0
            line_total = voucher.with_context(ctx).voucher_move_line_create(
                line_total, move.id, company_currency, current_currency)

            # Add tax correction to move line if any tax correction specified
            if voucher.tax_correction != 0.0:
                tax_move_line = self.env['account.move.line'].search(
                    [('move_id', '=', move.id), ('tax_line_id', '!=', False)],
                    limit=1)
                if len(tax_move_line):
                    tax_move_line.write({
                        'debit':
                        tax_move_line.debit + voucher.tax_correction
                        if tax_move_line.debit > 0 else 0,
                        'credit':
                        tax_move_line.credit + voucher.tax_correction
                        if tax_move_line.credit > 0 else 0
                    })

            # We post the voucher.
            voucher.write({
                'move_id': move.id,
                'state': 'posted',
                'number': move.name
            })
            move.post()
        return True

    @api.multi
    def _track_subtype(self, init_values):
        if 'state' in init_values:
            return 'account_voucher.mt_voucher_state_change'
        return super(AccountVoucher, self)._track_subtype(init_values)
Example #2
0
class edi_document(models.Model):
    _name = 'edi.document'
    _description = 'General EDI document'
    _inherit = 'mail.thread'

    body = fields.Text()
    sender = fields.Many2one('edi.document.partner')
    recipient = fields.Many2one('edi.document.partner')
    message_type = fields.Many2one('edi.message')
    model = fields.Char('Related Document Model', size=128, select=1)
    res_id = fields.Integer('Related Document ID', select=1)
    name = fields.Char()
    stage_id = fields.Many2one('edi.document.stage')

    @api.one
    def send_now(self):
        self.send_document()

    @api.one
    def send_document(self):
        ir_attachment_obj = self.env['ir.attachment']
        mail_template_obj = self.env['email.template']
        mail_mail_obj = self.env['mail.mail']
        ir_model_data_obj = self.env['ir.model.data']
        sent_stage = self.env.ref(
            'eintegration_edi_manager.edi_document_stage_tt_sent')
        ir_attachment = ir_attachment_obj.create({
            'datas':
            self.body.encode('base64'),
            'name':
            'invoic.xml',
            'datas_fname':
            'invoic.xml',
            'res_model':
            'edi.document',
            'res_id':
            self.id,
            'type':
            'binary',
        })
        edi_document_template_id = ir_model_data_obj.xmlid_to_res_id(
            'eintegration_edi_manager.edi_document_email_template')
        edi_document_template = mail_template_obj.browse(
            edi_document_template_id)
        overview_mail_id = edi_document_template.send_mail(self.id)
        overview_mail = mail_mail_obj.browse(overview_mail_id)
        overview_mail.attachment_ids = [(4, ir_attachment.id)]
        overview_mail.send()
        self.stage_id = sent_stage.id

    @api.model
    def cron_send_edi_documents(self):
        edi_document_obj = self.env['edi.document']
        to_be_sent_stage = self.env.ref(
            'eintegration_edi_manager.edi_document_stage_tt_to_be_sent')
        edi_document_ids = edi_document_obj.search([('stage_id', '=',
                                                     to_be_sent_stage.id)])
        for edi_document_id in edi_document_ids:
            edi_document_id.send_document()

    def create(self, vals):
        res = super(edi_document, self).create(vals)
        res.name = res.res_id

        #fill the 'body' field with the xml content based on an EDI template
        res._set_body_content()
        return res

    def _set_body_content(self):
        """
        Fills the 'body' field of the 'self' with an xml content based on an EDI template connected to a business object.
        """
        edi_template_obj = self.env['edi.template']
        if self.model:
            source_object_obj = self.env[self.model]
            edi_template = edi_template_obj.search([('model_name', '=',
                                                     self.model)])
            source_object_id = source_object_obj.browse(self.res_id)
            export_data = self._get_export_data(edi_template, source_object_id)
            self._create_xml_content(export_data)

    def _get_export_data(self, edi_template, source_object):
        """
        Returns the data from the source object based on the EDI template.
        The structure of the data follows the element hierarchy of the selected fields in the template.
        For example if an invoice has two lines then there will be two <invoice_line> elements inside the 'MESSAGEBODY' element.

        :param edi_template: EDI template which contains the set of field names which should be exported
        :return: list of dictionaries which contain the field names and values; top elements are the keys and the value is a dictionary that contains two lists - one for field names and one for the data)
        """
        #create a dictionary which will contain the names of the top elements as its keys
        #all the keys of names_dict will be direct child elements of the 'MESSAGEBODY' element
        exported_field_names = []
        names_dict = {}
        export_data = []
        for exported_field in edi_template.export_field_ids:
            element_names = exported_field.name.split('/')
            top_element_name = element_names[0]
            if top_element_name not in names_dict:
                names_dict[top_element_name] = []
            related_object = source_object
            for element_name in element_names:
                related_object = self._get_attr_value(related_object,
                                                      element_name)[0]
            related_fields = related_object.fields_get(False)
            for k, v in related_fields.iteritems():
                if self._is_basic_and_exportable(v):
                    exported_field_names.append(exported_field.name + '/' + k)
                    names_dict[top_element_name].append(exported_field.name +
                                                        '/' + k)

        #add the basic field from the source object into the list
        basic_fields = source_object.fields_get(False)
        for k, v in basic_fields.iteritems():
            if self._is_basic_and_exportable(v):
                exported_field_names.append(k)
                names_dict[k] = [k]

        #add the data of the exported fields
        for k, v in names_dict.iteritems():
            temp_data = self.export_db_data(source_object, v).get('datas', [])
            for temp_data_item in temp_data:
                export_data.append({'names': v, 'data': temp_data_item})

        return export_data

    def _is_basic_and_exportable(self, field_attributes):
        """
        Check if the field is non relation (basic) and exportable.

        :param field_attributes: a dictionary containing the field attributes
        """
        # If the 'exportable' attribute is missing then the field is exportable. The attribute is always present for fields which are not exportable.
        is_basic_and_exportable = not 'relation' in field_attributes and field_attributes.get(
            'exportable', True)
        return is_basic_and_exportable

    def _create_xml_content(self, export_data):
        """
        Creates an xml content based on the input data and the EDI report template and puts it into the 'body' field of the 'self'.

        :param export_data: a list of dictionaries with element names and values

        This method is called explicitly when the EDI document is created.
        """
        report_obj = self.env['report']
        self.body = report_obj.get_html(
            self, 'eintegration_edi_manager.edi_document_report_template')
        root = etree.fromstring(self.body.lstrip())
        message_body = root.find('.//MESSAGEBODY')
        element_list = []
        for export_data_item in export_data:
            top = None
            for exported_field in export_data_item['names']:
                element_names = exported_field.split('/')
                field_element = None
                parent_element = top
                for element_name in element_names:
                    if top is None:
                        field_element = etree.Element(element_name)
                        top = field_element
                    else:
                        field_element = parent_element.find(element_name)
                        if field_element is None:
                            field_element = parent_element if parent_element.tag == element_name else etree.SubElement(
                                parent_element, element_name)
                    parent_element = field_element
                data_value = export_data_item['data'].pop(0)
                if isinstance(data_value, (int, float)):
                    field_element.text = CDATA(str(data_value))
                else:
                    field_element.text = CDATA(data_value)
            element_list.append(top)
        for element in element_list:
            message_body.append(element)
        self.body = "<?xml version='1.0' encoding='ISO-8859-1'?>\n" + etree.tostring(
            root, pretty_print=True)

    def _get_attr_value(self, source_object, attr_name):
        """
        Returns the attribute value of the source object.

        :param source_object: the input object with attributes
        :param attr_name: the name of the desired attribute
        :return: the value of the source object's attribute
        """
        res_object = source_object
        res_value = []
        for res_id in res_object.ids:
            res_value.append(getattr(res_object.browse(res_id), attr_name))
        return res_value

    def export_records(self, source_object, fields):
        """
        Exports fields of the source_obect.

        :param source_object: the object which contains the source data
        :param fields: list of lists of fields which should be exported
        :return: list of lists of corresponding values
        """
        lines = []
        for record in source_object:
            # main line of record, initially empty
            current = [''] * len(fields)
            lines.append(current)

            # list of primary fields followed by secondary field(s)
            primary_done = []

            # process column by column
            for i, path in enumerate(fields):
                if not path:
                    continue

                name = path[0]
                if name in primary_done:
                    continue

                field = record._fields[name]
                value = record[name]

                # binary fields should be skipped
                if field.type == 'binary':
                    continue

                # this part could be simpler, but it has to be done this way
                # in order to reproduce the former behavior
                if not isinstance(value, models.BaseModel):
                    current[i] = field.convert_to_export(
                        value, source_object.env)
                else:
                    primary_done.append(name)

                    # This is a special case, its strange behavior is intended!
                    if field.type == 'many2many' and len(
                            path) > 1 and path[1] == 'id':
                        xml_ids = [r.id for r in value]
                        current[i] = ','.join(xml_ids) or False
                        continue

                    # recursively export the fields that follow name
                    fields2 = [(p[1:] if p and p[0] == name else [])
                               for p in fields]
                    lines2 = self.export_records(value, fields2)
                    if lines2:
                        # merge first line with record's main line
                        for j, val in enumerate(lines2[0]):
                            if val or isinstance(val, bool):
                                current[j] = val
                        # check value of current field
                        if not current[i] and not isinstance(
                                current[i], bool) and not current[i] == '':
                            # assign xml_ids, and forget about remaining lines
                            xml_ids = [item[1] for item in value.name_get()]
                            current[i] = ','.join(xml_ids)
                        else:
                            # append the other lines at the end
                            lines += lines2[1:]
                    else:
                        current[i] = False

        return lines

    def export_db_data(self, source_object, fields_to_export):
        """
        Export fields for the source object

        :param source_object: the object which contains the data for the export
        :param fields_to_export: list of fields
        :rtype: dictionary with a *datas* matrix
        """
        fields_to_export = map(models.fix_import_export_id_paths,
                               fields_to_export)
        return {'datas': self.export_records(source_object, fields_to_export)}

    def create_edi_document(self, res_object, partner_id):
        edi_document_obj = self.env['edi.document']
        edi_template_obj = self.env['edi.template']
        to_be_sent_stage = self.env.ref(
            'eintegration_edi_manager.edi_document_stage_tt_to_be_sent')
        model_name = res_object._model._name
        recipient = res_object.edi_recipient_id
        sender = self.get_document_partner(partner_id)
        if not sender:
            raise ValidationError(
                _('No GLN found for invoice issuer: %s') % (partner_id.name, ))
        edi_template = edi_template_obj.search([('model_name', '=', model_name)
                                                ])
        if not edi_template:
            raise ValidationError(
                _('No EDI template for %s found.') % (model_name))
        edi_document_obj.create({
            'res_id':
            res_object.id,
            'model':
            model_name,
            'stage_id':
            to_be_sent_stage.id,
            'sender':
            sender.id,
            'recipient':
            recipient.id,
            'message_type':
            edi_template.message_type.id or False,
        })
        res_object.message_post(_('EDI Document created'))

    @api.model
    def get_document_partner(self, partner_id):
        edi_document_partner_obj = self.env['edi.document.partner']
        edi_document_partner_ids = partner_id.edi_document_partner_ids or partner_id.parent_id.edi_document_partner_ids
        edi_document_partner_id = False
        company_id = False
        if not edi_document_partner_ids:
            if partner_id.is_company:
                company_id = partner_id
            elif partner_id.parent_id:
                company_id = partner_id.parent_id
            else:
                raise ValidationError(
                    _('No company contact found for %s.') % partner_id.name)
            if not hasattr(company_id, 'iln') or not company_id.iln:
                raise ValidationError(
                    _('No GLN found for %s. Please create a valid EDI recipient first.'
                      ) % (company_id.name))
            edi_document_partner_id = edi_document_partner_obj.create({
                'name':
                company_id.name,
                'partner_id':
                company_id.id,
                'gln':
                company_id.iln,
            })
        else:
            edi_document_partner_id = edi_document_partner_ids[0]
        return edi_document_partner_id
Example #3
0
class ir_attachment_attributes(models.Model):
    _name = "ir.attachment.attributes"

    attachment_id = fields.Many2one(comodel_name="ir.attachment")
    value = fields.Text(string="Value")
    label = fields.Many2one(comodel_name='ir.attachment.attributes.label')
Example #4
0
class TmsExpense(models.Model):
    _name = 'tms.expense'
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _description = 'Travel Expenses'
    _order = 'name desc'

    name = fields.Char(readonly=True)
    operating_unit_id = fields.Many2one('operating.unit',
                                        string='Operating Unit',
                                        required=True)
    employee_id = fields.Many2one('hr.employee', 'Driver', required=True)
    travel_ids = fields.Many2many('tms.travel', string='Travels')
    unit_id = fields.Many2one('fleet.vehicle', 'Unit', required=True)
    currency_id = fields.Many2one(
        'res.currency',
        'Currency',
        required=True,
        default=lambda self: self.env.user.company_id.currency_id)
    state = fields.Selection([('draft', 'Draft'), ('approved', 'Approved'),
                              ('confirmed', 'Confirmed'),
                              ('cancel', 'Cancelled')],
                             'Expense State',
                             readonly=True,
                             help="Gives the state of the Travel Expense. ",
                             default='draft')
    date = fields.Date('Date', required=True, default=fields.Date.today)
    expense_line_ids = fields.One2many('tms.expense.line', 'expense_id',
                                       'Expense Lines')
    amount_real_expense = fields.Float(compute='_compute_amount_real_expense',
                                       string='Expenses',
                                       store=True)
    amount_made_up_expense = fields.Float(
        compute='_compute_amount_made_up_expense',
        string='Fake Expenses',
        store=True)
    fuel_qty = fields.Float(compute='_compute_fuel_qty',
                            string='Fuel Qty',
                            store=True)
    amount_fuel = fields.Float(compute='_compute_amount_fuel',
                               string='Cost of Fuel',
                               store=True)
    amount_fuel_cash = fields.Float(compute='_compute_amount_fuel_cash',
                                    string='Fuel in Cash',
                                    store=True)
    amount_refund = fields.Float(compute='_compute_amount_refund',
                                 string='Refund',
                                 store=True)
    amount_other_income = fields.Float(compute='_compute_amount_other_income',
                                       string='Other Income',
                                       store=True)
    amount_salary = fields.Float(compute='_compute_amount_salary',
                                 string='Salary',
                                 store=True)
    amount_net_salary = fields.Float(compute='_compute_amount_net_salary',
                                     string='Net Salary',
                                     store=True)
    amount_salary_retention = fields.Float(
        compute='_compute_amount_salary_retention',
        string='Salary Retentions',
        store=True)
    amount_salary_discount = fields.Float(
        compute='_compute_amount_salary_discount',
        string='Salary Discounts',
        store=True)
    amount_advance = fields.Float(compute='_compute_amount_advance',
                                  string='Advances',
                                  store=True)
    amount_balance = fields.Float(compute='_compute_amount_balance',
                                  string='Balance',
                                  store=True)
    amount_balance2 = fields.Float(compute='_compute_amount_balance2',
                                   string='Balance',
                                   store=True)
    amount_tax_total = fields.Float(compute='_compute_amount_tax_total',
                                    string='Taxes (All)',
                                    store=True)
    amount_tax_real = fields.Float(compute='_compute_amount_tax_real',
                                   string='Taxes (Real)',
                                   store=True)
    amount_total_real = fields.Float(compute='_compute_amount_total_real',
                                     string='Total (Real)',
                                     store=True)
    amount_total_total = fields.Float(compute='_compute_amount_total_total',
                                      string='Total (All)',
                                      store=True)
    amount_subtotal_real = fields.Float(
        compute='_compute_amount_subtotal_real',
        string='SubTotal (Real)',
        store=True)
    amount_subtotal_total = fields.Float(
        string='SubTotal (All)',
        compute='_compute_amount_subtotal_total',
        store=True)
    vehicle_id = fields.Many2one('fleet.vehicle', 'Vehicle')
    last_odometer = fields.Float('Last Read')
    vehicle_odometer = fields.Float('Vehicle Odometer')
    current_odometer = fields.Float(string='Current Real',
                                    compute='_compute_current_odometer')
    distance_routes = fields.Float(compute='_compute_distance_routes',
                                   string='Distance from routes',
                                   help="Routes Distance")
    distance_real = fields.Float(
        string='Distance Real',
        help="Route obtained by electronic reading and/or GPS")
    odometer_log_id = fields.Many2one('fleet.vehicle.odometer',
                                      'Odometer Record')
    global_fuel_efficiency_routes = fields.Float(
        # compute=_get_fuel_efficiency,
        string='Global Fuel Efficiency Routes')
    loaded_fuel_efficiency = fields.Float('Loaded Fuel Efficiency')
    unloaded_fuel_efficiency = fields.Float('Unloaded Fuel Efficiency')
    notes = fields.Text()
    move_id = fields.Many2one(
        'account.move',
        'Journal Entry',
        readonly=True,
        help="Link to the automatically generated Journal Items.")
    paid = fields.Boolean(compute='_compute_paid', store=True, readonly=True)
    advance_ids = fields.One2many('tms.advance',
                                  'expense_id',
                                  string='Advances',
                                  readonly=True)
    fuel_qty_real = fields.Float(
        'Fuel Qty Real',
        help="Fuel Qty computed based on Distance Real and Global Fuel "
        "Efficiency Real obtained by electronic reading and/or GPS")
    fuel_diff = fields.Float(
        string="Fuel Difference",
        help="Fuel Qty Difference between Fuel Vouchers + Fuel Paid in Cash "
        "versus Fuel qty computed based on Distance Real and Global Fuel "
        "Efficiency Real obtained by electronic reading and/or GPS"
        # compute=_get_fuel_diff
    )
    global_fuel_efficiency_real = fields.Float(
        # compute=_get_fuel_diff,
        string='Global Fuel Efficiency Real')
    fuel_log_ids = fields.One2many('fleet.vehicle.log.fuel',
                                   'expense_id',
                                   string='Fuel Vouchers')
    start_date = fields.Datetime(string="Start Date", )
    end_date = fields.Datetime(string="End Date", )
    fuel_efficiency = fields.Float(string="Fuel Efficiency",
                                   readonly=True,
                                   compute="_compute_fuel_efficiency")
    payment_move_id = fields.Many2one('account.move',
                                      string='Payment Entry',
                                      readonly=True)
    travel_days = fields.Char(
        string='Travel Days',
        compute='_compute_travel_days',
    )

    @api.depends('start_date', 'end_date')
    def _compute_travel_days(self):
        for rec in self:
            if rec.start_date and rec.end_date:
                strp_start_date = datetime.datetime.strptime(
                    rec.start_date, "%Y-%m-%d %H:%M:%S")
                strp_end_date = datetime.datetime.strptime(
                    rec.end_date, "%Y-%m-%d %H:%M:%S")
                difference = strp_end_date - strp_start_date
                hours = int(difference.seconds / 3600)
                mins = int((difference.seconds - (hours * 3600)) / 60)
                seconds = difference.seconds - ((hours * 3600) + (mins * 60))
                if hours < 10:
                    hours = '0' + str(hours)
                if mins < 10:
                    mins = '0' + str(mins)
                if seconds < 10:
                    seconds = '0' + str(seconds)
                total_string = (str(difference.days) + _('Day(s), ') +
                                str(hours) + ':' + str(mins) + ':' +
                                str(seconds))
                rec.travel_days = total_string

    @api.depends('payment_move_id')
    def _compute_paid(self):
        for rec in self:
            if rec.payment_move_id:
                rec.paid = True

    @api.depends('fuel_qty', 'distance_real')
    def _compute_fuel_efficiency(self):
        for rec in self:
            if rec.distance_real and rec.fuel_qty:
                rec.fuel_efficiency = rec.distance_real / rec.fuel_qty

    @api.depends('expense_line_ids')
    def _compute_fuel_qty(self):
        for rec in self:
            for line in rec.expense_line_ids:
                if line.line_type == 'fuel':
                    rec.fuel_qty += line.product_qty

    @api.depends('travel_ids', 'expense_line_ids')
    def _compute_amount_fuel(self):
        for rec in self:
            rec.amount_fuel = 0.0
            for line in rec.fuel_log_ids:
                rec.amount_fuel += (line.price_subtotal +
                                    line.special_tax_amount)

    @api.depends('expense_line_ids')
    def _compute_amount_fuel_cash(self):
        for rec in self:
            rec.amount_fuel_cash = 0.0
            for line in rec.expense_line_ids:
                if line.line_type == 'fuel_cash':
                    rec.amount_fuel_cash += (line.price_subtotal +
                                             line.special_tax_amount)

    @api.depends('expense_line_ids')
    def _compute_amount_refund(self):
        for rec in self:
            rec.amount_refund = 0.0
            for line in rec.expense_line_ids:
                if line.line_type == 'refund':
                    rec.amount_refund += line.price_total

    @api.depends('expense_line_ids')
    def _compute_amount_other_income(self):
        for rec in self:
            rec.amount_other_income = 0.0
            for line in rec.expense_line_ids:
                if line.line_type == 'other_income':
                    rec.amount_other_income += line.price_total

    @api.depends('expense_line_ids')
    def _compute_amount_salary(self):
        for rec in self:
            rec.amount_salary = 0.0
            for line in rec.expense_line_ids:
                if line.line_type == 'salary':
                    rec.amount_salary += line.price_total

    @api.depends('expense_line_ids')
    def _compute_amount_salary_discount(self):
        for rec in self:
            rec.amount_salary_discount = 0
            for line in rec.expense_line_ids:
                if line.line_type == 'salary_discount':
                    rec.amount_salary_discount += line.price_total

    @api.depends('expense_line_ids')
    def _compute_amount_made_up_expense(self):
        for rec in self:
            rec.amount_made_up_expense = 0
            for line in rec.expense_line_ids:
                if line.line_type == 'made_up_expense':
                    rec.amount_made_up_expense += line.price_total

    @api.depends('expense_line_ids')
    def _compute_amount_real_expense(self):
        for rec in self:
            rec.amount_real_expense = 0
            for line in rec.expense_line_ids:
                if line.line_type == 'real_expense':
                    rec.amount_real_expense += line.price_subtotal

    @api.depends('travel_ids', 'expense_line_ids')
    def _compute_amount_subtotal_real(self):
        for rec in self:
            rec.amount_subtotal_real = (rec.amount_salary +
                                        rec.amount_salary_discount +
                                        rec.amount_real_expense +
                                        rec.amount_salary_retention +
                                        rec.amount_refund +
                                        rec.amount_fuel_cash +
                                        rec.amount_other_income)

    @api.depends('travel_ids', 'expense_line_ids')
    def _compute_amount_total_real(self):
        for rec in self:
            rec.amount_total_real = (rec.amount_subtotal_real +
                                     rec.amount_tax_real)

    @api.depends('travel_ids', 'expense_line_ids')
    def _compute_amount_balance(self):
        for rec in self:
            rec.amount_balance = (rec.amount_total_real - rec.amount_advance)

    @api.depends('travel_ids')
    def _compute_amount_net_salary(self):
        for rec in self:
            rec.amount_net_salary = 1.0

    @api.depends('expense_line_ids')
    def _compute_amount_salary_retention(self):
        for rec in self:
            for line in rec.expense_line_ids:
                if line.line_type == 'salary_retention':
                    rec.amount_salary_retention += line.price_total

    @api.depends('travel_ids', 'expense_line_ids')
    def _compute_amount_advance(self):
        for rec in self:
            rec.amount_advance = 0
            for travel in rec.travel_ids:
                for advance in travel.advance_ids:
                    if advance.payment_move_id:
                        rec.amount_advance += advance.amount

    @api.depends('travel_ids')
    def _compute_amount_balance2(self):
        for rec in self:
            rec.amount_balance2 = 1.0

    @api.depends('travel_ids', 'expense_line_ids')
    def _compute_amount_tax_total(self):
        for rec in self:
            rec.amount_tax_total = 0
            for travel in rec.travel_ids:
                for fuel_log in travel.fuel_log_ids:
                    rec.amount_tax_total += fuel_log.tax_amount
            rec.amount_tax_total += rec.amount_tax_real

    @api.depends('expense_line_ids')
    def _compute_amount_tax_real(self):
        for rec in self:
            rec.amount_tax_real = 0
            for line in rec.expense_line_ids:
                if line.line_type == 'real_expense':
                    rec.amount_tax_real += line.tax_amount

    @api.depends('travel_ids', 'expense_line_ids')
    def _compute_amount_subtotal_total(self):
        for rec in self:
            rec.amount_subtotal_total = 0
            for travel in rec.travel_ids:
                for fuel_log in travel.fuel_log_ids:
                    rec.amount_subtotal_total += (fuel_log.price_subtotal +
                                                  fuel_log.special_tax_amount)
            for line in rec.expense_line_ids:
                if line.line_type == 'real_expense':
                    rec.amount_subtotal_total += line.price_subtotal
            rec.amount_subtotal_total += rec.amount_balance

    @api.depends('travel_ids', 'expense_line_ids')
    def _compute_amount_total_total(self):
        for rec in self:
            rec.amount_total_total = (rec.amount_subtotal_total +
                                      rec.amount_tax_total +
                                      rec.amount_made_up_expense)

    @api.depends('travel_ids')
    def _compute_distance_routes(self):
        distance = 0.0
        for rec in self:
            for travel in rec.travel_ids:
                distance += travel.route_id.distance
            rec.distance_routes = distance

    @api.depends('travel_ids')
    def _compute_current_odometer(self):
        for rec in self:
            rec.current_odometer = rec.unit_id.odometer

    @api.model
    def create(self, values):
        expense = super(TmsExpense, self).create(values)
        sequence = expense.operating_unit_id.expense_sequence_id
        expense.name = sequence.next_by_id()
        return expense

    @api.multi
    def write(self, values):
        for rec in self:
            res = super(TmsExpense, self).write(values)
            rec.get_travel_info()
            return res

    @api.multi
    def unlink(self):
        for rec in self:
            if rec.state == 'confirmed':
                raise ValidationError(
                    _('You can not delete a travel expense'
                      'in status confirmed'))
            else:
                travels = self.env['tms.travel'].search([('expense_id', '=',
                                                          rec.id)])
                travels.write({'expense_id': False, 'state': 'done'})
                advances = self.env['tms.advance'].search([('expense_id', '=',
                                                            rec.id)])
                advances.write({'expense_id': False, 'state': 'confirmed'})
                fuel_logs = self.env['fleet.vehicle.log.fuel'].search([
                    ('expense_id', '=', rec.id)
                ])
                fuel_logs.write({'expense_id': False, 'state': 'confirmed'})
                return super(TmsExpense, self).unlink()

    @api.multi
    def action_approved(self):
        for rec in self:
            message = _('<b>Expense Approved.</b></br><ul>'
                        '<li><b>Approved by: </b>%s</li>'
                        '<li><b>Approved at: </b>%s</li>'
                        '</ul>') % (self.env.user.name, fields.Date.today())
            rec.message_post(body=message)
        self.state = 'approved'

    @api.multi
    def action_draft(self):
        for rec in self:
            message = _('<b>Expense Drafted.</b></br><ul>'
                        '<li><b>Drafted by: </b>%s</li>'
                        '<li><b>Drafted at: </b>%s</li>'
                        '</ul>') % (self.env.user.name, fields.Date.today())
            rec.message_post(body=message)
        self.state = 'draft'

    @api.multi
    def action_confirm(self):
        for rec in self:
            move_obj = self.env['account.move']
            journal_id = rec.operating_unit_id.expense_journal_id.id
            advance_account_id = (rec.employee_id.tms_advance_account_id.id)
            negative_account = (
                rec.employee_id.tms_expense_negative_account_id.id)
            driver_account_payable = (
                rec.employee_id.address_home_id.property_account_payable_id.id)
            if not journal_id:
                raise ValidationError(
                    _('Warning! The expense does not have a journal'
                      ' assigned. \nCheck if you already set the '
                      'journal for expense moves in the Operating Unit.'))
            if not driver_account_payable:
                raise ValidationError(
                    _('Warning! The driver does not have a home address'
                      ' assigned. \nCheck if you already set the '
                      'home address for the employee.'))
            if not advance_account_id:
                raise ValidationError(
                    _('Warning! You must have configured the accounts'
                      'of the tms for the Driver'))
            move_lines = []
            # We check if the advance amount is higher than zero to create
            # a move line
            if rec.amount_advance > 0:
                move_line = (0, 0, {
                    'name': _('Advance Discount'),
                    'ref': (rec.name),
                    'account_id': advance_account_id,
                    'narration': rec.name,
                    'debit': 0.0,
                    'credit': rec.amount_advance,
                    'journal_id': journal_id,
                    'partner_id': rec.employee_id.address_home_id.id,
                    'operating_unit_id': rec.operating_unit_id.id,
                })
                move_lines.append(move_line)
            invoices = []

            for line in rec.expense_line_ids:
                if line.line_type == 'fuel' and not line.control:
                    self.env['fleet.vehicle.log.fuel'].create({
                        'operating_unit_id':
                        rec.operating_unit_id.id,
                        'travel_id':
                        line.travel_id.id,
                        'vehicle_id':
                        line.travel_id.unit_id.id,
                        'product_id':
                        line.product_id.id,
                        'price_unit':
                        line.unit_price,
                        'price_subtotal':
                        line.price_subtotal,
                        'vendor_id':
                        line.partner_id.id,
                        'product_qty':
                        line.product_qty,
                        'tax_amount':
                        line.tax_amount,
                        'state':
                        'closed',
                        'employee_id':
                        rec.employee_id.id,
                        'price_total':
                        line.price_total,
                        'date':
                        str(fields.Date.today()),
                        'expense_control':
                        True,
                        'expense_id':
                        rec.id,
                        'ticket_number':
                        line.invoice_number,
                    })
                    line.control = True
                # We only need all the lines except the fuel and the
                # made up expenses
                if line.line_type not in ('made_up_expense', 'fuel'):
                    product_account = (
                        negative_account if
                        (line.product_id.tms_product_category
                         == 'negative_balance') else
                        (line.product_id.property_account_expense_id.id) if
                        (line.product_id.property_account_expense_id.id) else
                        (line.product_id.categ_id.
                         property_account_expense_categ_id.id) if
                        (line.product_id.categ_id.
                         property_account_expense_categ_id.id) else False)
                    if not product_account:
                        raise ValidationError(
                            _('Warning ! Expense Account is not defined for'
                              ' product %s') % (line.product_id.name))
                    inv_id = False
                    # We check if the expense line is an invoice to create it
                    # and make the move line based in the total with taxes
                    if line.is_invoice:
                        inv_id = self.create_supplier_invoice(line)
                        inv_id.signal_workflow('invoice_open')
                        invoices.append(inv_id)
                        move_line = (0, 0, {
                            'name': (rec.name + ' ' + line.name +
                                     ' - Inv ID - ' + str(inv_id.id)),
                            'ref':
                            (rec.name + ' - Inv ID - ' + str(inv_id.id)),
                            'account_id':
                            (line.partner_id.property_account_payable_id.id),
                            'debit': (line.price_total
                                      if line.price_total > 0.0 else 0.0),
                            'credit': (line.price_total
                                       if line.price_total <= 0.0 else 0.0),
                            'journal_id':
                            journal_id,
                            'partner_id':
                            line.partner_id.id,
                            'operating_unit_id':
                            rec.operating_unit_id.id,
                        })
                        move_lines.append(move_line)
                    # if the expense line not be a invoice we make the move
                    # line based in the subtotal
                    else:
                        move_line = (0, 0, {
                            'name':
                            rec.name + ' ' + line.name,
                            'ref':
                            rec.name,
                            'account_id':
                            product_account,
                            'debit': (line.price_subtotal
                                      if line.price_subtotal > 0.0 else 0.0),
                            'credit':
                            (line.price_subtotal *
                             -1.0 if line.price_subtotal <= 0.0 else 0.0),
                            'journal_id':
                            journal_id,
                            'partner_id':
                            rec.employee_id.address_home_id.id,
                            'operating_unit_id':
                            rec.operating_unit_id.id,
                        })
                        move_lines.append(move_line)
                    # we check the line tax to create the move line if
                    # the line not be an invoice
                    for tax in line.tax_ids:
                        tax_account = tax.account_id.id
                        if not tax_account:
                            raise ValidationError(
                                _('Warning !'),
                                _('Tax Account is not defined for '
                                  'Tax %s (id:%d)') % (
                                      tax.name,
                                      tax.id,
                                  ))
                        tax_amount = line.tax_amount
                        # We create a move line for the line tax
                        if not line.is_invoice:
                            move_line = (0, 0, {
                                'name': (rec.name + ' ' + line.name),
                                'ref':
                                rec.name,
                                'account_id':
                                tax_account,
                                'debit':
                                (tax_amount if tax_amount > 0.0 else 0.0),
                                'credit':
                                (tax_amount if tax_amount <= 0.0 else 0.0),
                                'journal_id':
                                journal_id,
                                'partner_id':
                                (rec.employee_id.address_home_id.id),
                                'operating_unit_id':
                                rec.operating_unit_id.id,
                            })
                            move_lines.append(move_line)
            # Here we check if the balance is positive or negative to create
            # the move line with the correct values
            if rec.amount_balance < 0:
                move_line = (0, 0, {
                    'name': _('Negative Balance'),
                    'ref': rec.name,
                    'account_id': negative_account,
                    'debit': rec.amount_balance * -1.0,
                    'credit': 0.0,
                    'journal_id': journal_id,
                    'partner_id': rec.employee_id.address_home_id.id,
                    'operating_unit_id': rec.operating_unit_id.id,
                })
                move_lines.append(move_line)
            else:
                move_line = (0, 0, {
                    'name': rec.name,
                    'account_id': driver_account_payable,
                    'debit': 0.0,
                    'credit': rec.amount_balance,
                    'journal_id': journal_id,
                    'partner_id': rec.employee_id.address_home_id.id,
                    'operating_unit_id': rec.operating_unit_id.id,
                })
                move_lines.append(move_line)
            move = {
                'date': fields.Date.today(),
                'journal_id': journal_id,
                'name': rec.name,
                'line_ids': [line for line in move_lines],
                'partner_id': self.env.user.company_id.id,
                'operating_unit_id': rec.operating_unit_id.id,
            }
            move_id = move_obj.create(move)
            if not move_id:
                raise ValidationError(
                    _('An error has occurred in the creation'
                      ' of the accounting move. '))
            move_id.post()
            # Here we reconcile the invoices with the corresponding
            # move line
            self.reconcile_supplier_invoices(invoices, move_id)
            rec.write({'move_id': move_id.id, 'state': 'confirmed'})
            message = _('<b>Expense Confirmed.</b></br><ul>'
                        '<li><b>Confirmed by: </b>%s</li>'
                        '<li><b>Confirmed at: </b>%s</li>'
                        '</ul>') % (self.env.user.name, fields.Date.today())
            rec.message_post(body=message)

    @api.multi
    def action_cancel(self):
        for rec in self:
            if rec.paid:
                raise ValidationError(
                    _('You cannot cancel an expense that is paid.'))
            if rec.state == 'confirmed':
                for line in rec.expense_line_ids:
                    if line.invoice_id and line.line_type != 'fuel':
                        for move_line in line.invoice_id.move_id.line_ids:
                            if move_line.account_id.reconcile:
                                move_line.remove_move_reconcile()
                        line.invoice_id.write({
                            # TODO Make a separate module to delete oml data
                            'cfdi_fiscal_folio': False,
                            'xml_signed': False,
                            'reference': False,
                        })
                        line.invoice_id.signal_workflow('invoice_cancel')
                        line.invoice_id = False
                if rec.move_id.state == 'posted':
                    rec.move_id.button_cancel()
                rec.move_id.unlink()
            rec.state = 'cancel'

    @api.multi
    def get_travel_info(self):
        for rec in self:
            exp_no_travel = rec.expense_line_ids.search([
                ('expense_id', '=', rec.id), ('travel_id', '=', False)
            ]).ids
            rec.expense_line_ids.search([
                ('expense_id', '=', rec.id),
                ('travel_id', 'not in', rec.travel_ids.ids),
                ('id', 'not in', exp_no_travel)
            ]).unlink()
            rec.expense_line_ids.search([('expense_id', '=', rec.id),
                                         ('control', '=', True),
                                         ('line_type', '!=', 'fuel')
                                         ]).unlink()
            travels = self.env['tms.travel'].search([('expense_id', '=',
                                                      rec.id)])
            travels.write({'expense_id': False, 'state': 'done'})
            advances = self.env['tms.advance'].search([('expense_id', '=',
                                                        rec.id)])
            for adv in advances:
                if adv.state != 'cancel':
                    adv.state = 'confirmed'
                adv.expense_id = False
            fuel_logs = self.env['fleet.vehicle.log.fuel'].search([
                ('expense_id', '=', rec.id)
            ])
            fuel_logs.write({'expense_id': False, 'state': 'confirmed'})
            for travel in rec.travel_ids:
                travel.write({'state': 'closed', 'expense_id': rec.id})
                for advance in travel.advance_ids:
                    if advance.state not in ('confirmed', 'cancel'):
                        raise ValidationError(
                            _('Oops! All the advances must be confirmed'
                              ' or cancelled \n '
                              'Name of advance not confirmed or cancelled: ' +
                              advance.name + '\n State: ' + advance.state))
                    if not advance.paid:
                        if advance.move_id.matched_percentage == 1.0:
                            advance_move = advance.move_id.line_ids[-1]
                            if advance_move.credit > 0:
                                move_lines = advance.move_id.line_ids[-1]
                                reconcile_move = move_lines.full_reconcile_id
                                for line in reconcile_move.reconciled_line_ids:
                                    if line.journal_id.type == 'bank':
                                        move_id = line.move_id.id
                            advance.write({
                                'paid': True,
                                'payment_move_id': move_id
                            })
                    if not advance.paid and advance.state == 'confirmed':
                        raise ValidationError(
                            _('Oops! All the advances must be paid'
                              '\n Name of advance not paid: ' + advance.name))
                    if (advance.auto_expense and advance.state == 'confirmed'):
                        rec.expense_line_ids.create({
                            'name':
                            _("Advance: ") + str(advance.name),
                            'travel_id':
                            travel.id,
                            'expense_id':
                            rec.id,
                            'line_type':
                            "real_expense",
                            'product_id':
                            advance.product_id.id,
                            'product_qty':
                            1.0,
                            'unit_price':
                            advance.amount,
                            'control':
                            True
                        })
                    if advance.state != 'cancel':
                        advance.write({
                            'state': 'closed',
                            'expense_id': rec.id
                        })
                for fuel_log in travel.fuel_log_ids:
                    if (fuel_log.state != 'confirmed'
                            and fuel_log.state != 'closed'):
                        raise ValidationError(
                            _('Oops! All the voucher must be confirmed'
                              '\n Name of voucher not confirmed: ' +
                              fuel_log.name + '\n State: ' + fuel_log.state))
                    else:
                        if not fuel_log.expense_control:
                            rec.expense_line_ids.create({
                                'name':
                                _("Fuel voucher: ") + str(fuel_log.name),
                                'travel_id':
                                travel.id,
                                'expense_id':
                                rec.id,
                                'line_type':
                                'fuel',
                                'product_id':
                                fuel_log.product_id.id,
                                'product_qty':
                                fuel_log.product_qty,
                                'product_uom_id':
                                (fuel_log.product_id.uom_id.id),
                                'unit_price':
                                fuel_log.price_total,
                                'is_invoice':
                                fuel_log.invoice_paid,
                                'invoice_id':
                                fuel_log.invoice_id.id,
                                'control':
                                True,
                            })
                        fuel_log.write({
                            'state': 'closed',
                            'expense_id': rec.id
                        })
                product_id = self.env['product.product'].search([
                    ('tms_product_category', '=', 'salary')
                ])
                if not product_id:
                    raise ValidationError(
                        _('Oops! You must create a product for the'
                          ' diver salary with the Salary TMS '
                          'Product Category'))
                rec.expense_line_ids.create({
                    'name':
                    _("Salary per travel: ") + str(travel.name),
                    'travel_id':
                    travel.id,
                    'expense_id':
                    rec.id,
                    'line_type':
                    "salary",
                    'product_qty':
                    1.0,
                    'product_uom_id':
                    product_id.uom_id.id,
                    'product_id':
                    product_id.id,
                    'unit_price':
                    rec.get_driver_salary(travel),
                    'control':
                    True
                })

    @api.depends('travel_ids')
    def get_driver_salary(self, travel):
        for rec in self:
            driver_salary = 0.0
            for waybill in travel.waybill_ids:
                income = 0.0
                for line in waybill.waybill_line_ids:
                    if line.product_id.apply_for_salary:
                        income += line.price_subtotal
                if waybill.currency_id.name == 'USD':
                    income = (income *
                              self.env.user.company_id.expense_currency_rate)
                if len(waybill.driver_factor_ids) > 0:
                    for factor in waybill.driver_factor_ids:
                        driver_salary += factor.get_amount(
                            weight=waybill.product_weight,
                            distance=waybill.distance_route,
                            distance_real=waybill.distance_real,
                            qty=waybill.product_qty,
                            volume=waybill.product_volume,
                            income=income,
                            employee=rec.employee_id)
                elif len(travel.driver_factor_ids) > 0:
                    for factor in travel.driver_factor_ids:
                        driver_salary += factor.get_amount(
                            weight=waybill.product_weight,
                            distance=waybill.distance_route,
                            distance_real=waybill.distance_real,
                            qty=waybill.product_qty,
                            volume=waybill.product_volume,
                            income=income,
                            employee=rec.employee_id)
                else:
                    raise ValidationError(
                        _('Oops! You have not defined a Driver factor in '
                          'the Travel or the Waybill\nTravel: %s' %
                          travel.name))
            return driver_salary

    @api.multi
    def create_supplier_invoice(self, line):
        journal_id = self.operating_unit_id.expense_journal_id.id
        product_account = (
            line.product_id.product_tmpl_id.property_account_expense_id.id)
        if not product_account:
            product_account = (
                line.product_id.categ_id.property_account_expense_categ_id.id)
        if not product_account:
            raise ValidationError(
                _('Error !'),
                _('There is no expense account defined for this'
                  ' product: "%s") % (line.product_id.name'))
        if not journal_id:
            raise ValidationError(
                _('Error !',
                  'You have not defined Travel Expense Supplier Journal...'))
        invoice_line = (0, 0, {
            'name':
            _('%s (TMS Expense Record %s)') %
            (line.product_id.name, line.expense_id.name),
            'origin':
            line.expense_id.name,
            'account_id':
            product_account,
            'quantity':
            line.product_qty,
            'price_unit':
            line.unit_price,
            'invoice_line_tax_ids': [(6, 0, [x.id for x in line.tax_ids])],
            'uom_id':
            line.product_uom_id.id,
            'product_id':
            line.product_id.id,
        })
        notes = line.expense_id.name + ' - ' + line.product_id.name
        partner_account = line.partner_id.property_account_payable_id.id
        invoice = {
            'origin':
            line.expense_id.name,
            'type':
            'in_invoice',
            'journal_id':
            journal_id,
            'reference':
            line.invoice_number,
            'account_id':
            partner_account,
            'partner_id':
            line.partner_id.id,
            'invoice_line_ids': [invoice_line],
            'currency_id':
            line.expense_id.currency_id.id,
            'payment_term_id':
            (line.partner_id.property_supplier_payment_term_id.id
             if line.partner_id.property_supplier_payment_term_id else False),
            'fiscal_position_id':
            (line.partner_id.property_account_position_id.id or False),
            'comment':
            notes,
            'operating_unit_id':
            line.expense_id.operating_unit_id.id,
        }
        invoice_id = self.env['account.invoice'].create(invoice)
        line.write({'invoice_id': invoice_id.id})
        return invoice_id

    @api.multi
    def reconcile_supplier_invoices(self, invoice_ids, move_id):
        move_line_obj = self.env['account.move.line']
        for invoice in invoice_ids:
            move_ids = []
            invoice_str_id = str(invoice.id)
            expense_move_line = move_line_obj.search([
                ('move_id', '=', move_id.id), ('name', 'ilike', invoice_str_id)
            ])
            if not expense_move_line:
                raise ValidationError(
                    _('Error ! Move line was not found,'
                      ' please check your data.'))
            move_ids.append(expense_move_line.id)
            for move_line in invoice.move_id.line_ids:
                if move_line.account_id.internal_type == 'payable':
                    invoice_move_line_id = move_line
            move_ids.append(invoice_move_line_id.id)
            reconcile_ids = move_line_obj.browse(move_ids)
            reconcile_ids.reconcile()
        return True

    @api.onchange('operating_unit_id', 'unit_id')
    def _onchange_operating_unit_id(self):
        travels = self.env['tms.travel'].search([
            ('operating_unit_id', '=', self.operating_unit_id.id),
            ('state', '=', 'done'), ('unit_id', '=', self.unit_id.id)
        ])
        self.employee_id = False
        return {
            'domain': {
                'employee_id':
                [('id', 'in', [x.employee_id.id for x in travels]),
                 ('driver', '=', True)]
            }
        }

    @api.multi
    def get_amount_total(self):
        for rec in self:
            amount_subtotal = 0.0
            for line in rec.expense_line_ids:
                if line.line_type in ['real_expense', 'fuel', 'fuel_cash']:
                    amount_subtotal += line.price_subtotal
            return amount_subtotal

    @api.multi
    def get_amount_tax(self):
        for rec in self:
            tax_amount = 0.0
            for line in rec.expense_line_ids:
                if line.line_type in ['real_expense', 'fuel', 'fuel_cash']:
                    tax_amount += line.tax_amount
            return tax_amount

    @api.multi
    def get_value(self, type_line):
        for rec in self:
            value = 0.0
            for line in rec.expense_line_ids:
                if line.line_type == type_line:
                    value += line.price_total
        return value
class ProjectLifecycle(models.Model):
    _name = 'compassion.project.ile'
    _description = 'Project lifecycle event'
    _order = 'date desc'

    project_id = fields.Many2one('compassion.project',
                                 required=True,
                                 ondelete='cascade',
                                 readonly=True)
    date = fields.Date(readonly=True, default=fields.Date.today)
    type = fields.Selection([
        ('Suspension', 'Suspension'),
        ('Reactivation', 'Reactivation'),
        ('Transition', 'Transition'),
    ],
                            readonly=True)
    action_plan = fields.Text(readonly=True)

    # Reactivation
    ##############
    icp_improvement_desc = fields.Text(readonly=True)

    # Suspension
    ############
    suspension_start_date = fields.Date(readonly=True)
    suspension_end_date = fields.Date(readonly=True)
    suspension_detail = fields.Char(readonly=True)
    suspension_reason_ids = fields.Many2many('icp.lifecycle.reason',
                                             string='Suspension reason',
                                             readonly=True)

    hold_cdsp_funds = fields.Boolean(readonly=True)
    hold_csp_funds = fields.Boolean(readonly=True)
    hold_gifts = fields.Boolean(readonly=True)
    hold_s2b_letters = fields.Boolean(readonly=True)
    hold_b2s_letters = fields.Boolean(readonly=True)
    hold_child_updates = fields.Boolean(readonly=True)
    is_beneficiary_information_updates_withheld = fields.Boolean(readonly=True)

    extension_1 = fields.Boolean(help='Suspension is extended by 30 days',
                                 readonly=True)
    extension_1_reason = fields.Text(readonly=True)
    extension_2 = fields.Boolean(
        help='Suspension is extended by additional 30 days (60 in total)',
        readonly=True)
    extension_2_reason = fields.Text(readonly=True)

    # Transition
    ############
    transition_date = fields.Date(readonly=True)
    transition_complete = fields.Boolean(readonly=True)
    details = fields.Text(readonly=True)
    transition_reason_ids = fields.Many2many('icp.lifecycle.reason',
                                             string='Transition reason',
                                             readonly=True)
    projected_transition_date = fields.Date(readonly=True)
    future_involvement_ids = fields.Many2many('icp.involvement',
                                              string='Future involvement',
                                              readonly=True)
    # celebration_details = fields.Char(readonly=True)
    # relationship_strengths = fields.Char(readonly=True)

    # Translation
    ############
    translation_completed_fields = fields.Selection([
        ('Action Plan', 'Action Plan'),
        ('Suspension Details', 'Suspension Details'),
        ('Transition Details', 'Transition Details'),
    ],
                                                    readonly=True)
    translation_required_fields = fields.Selection([
        ('Action Plan', 'Action Plan'),
        ('Suspension Details', 'Suspension Details'),
        ('Transition Details', 'Transition Details'),
    ],
                                                   readonly=True)
    translation_status = fields.Selection([
        ('Draft', 'Draft'),
        ('Ready For Translation', 'Ready For Translation'),
        ('Translation Complete', 'Translation Complete'),
    ],
                                          readonly=True)

    name = fields.Char(readonly=True)
    reactivation_date = fields.Date(readonly=True)
    project_status = fields.Selection([
        ('Active', 'Active'),
        ('Phase Out', 'Phase Out'),
        ('Suspended', 'Suspended'),
        ('Transitioned', 'Transitioned'),
    ],
                                      readonly=True)

    @api.model
    def process_commkit(self, commkit_data):
        project_mapping = mapping.new_onramp_mapping(self._name, self.env,
                                                     'new_project_lifecyle')

        lifecycle_ids = list()
        for single_data in commkit_data.get('ICPLifecycleEventList',
                                            [commkit_data]):
            vals = project_mapping.get_vals_from_connect(single_data)
            lifecycle = self.create(vals)
            lifecycle_ids.append(lifecycle.id)

            lifecycle.project_id.status_date = fields.Date.today()

        return lifecycle_ids
Example #6
0
class reconcile_order(models.Model):
    _name = 'reconcile.order'
    _description = u'核销单'

    TYPE_SELECTION = [
        ('adv_pay_to_get', u'预收冲应收'),
        ('adv_get_to_pay', u'预付冲应付'),
        ('get_to_pay', u'应收冲应付'),
        ('get_to_get', u'应收转应收'),
        ('pay_to_pay', u'应付转应付'),
    ]

    @api.model
    def create(self, values):
        # 生成订单编号
        if values.get('name', '/') == '/':
            values.update({'name': self.env['ir.sequence'].get(self._name)})

        return super(reconcile_order, self).create(values)

    @api.multi
    def unlink(self):
        for order in self:
            if order.state == 'done':
                raise except_orm(u'错误', u'不可以删除已经审核的单据')

        return super(reconcile_order, self).unlink()

    state = fields.Selection([
        ('draft', u'未审核'),
        ('done', u'已审核'),
    ],
                             string=u'状态',
                             readonly=True,
                             default='draft',
                             copy=False,
                             help=u'核销单状态标识,新建时状态为未审核;审核后状态为已审核')
    partner_id = fields.Many2one('partner',
                                 string=u'业务伙伴',
                                 required=True,
                                 readonly=True,
                                 ondelete='restrict',
                                 states={'draft': [('readonly', False)]},
                                 help=u'该单据对应的业务伙伴,与业务类型一起带出待核销的明细行')
    to_partner_id = fields.Many2one('partner',
                                    string=u'转入往来单位',
                                    readonly=True,
                                    ondelete='restrict',
                                    states={'draft': [('readonly', False)]},
                                    help=u'应收转应收、应付转应付时对应的转入业务伙伴,'
                                    u'订单审核时会影响该业务伙伴的应收/应付')
    advance_payment_ids = fields.One2many(
        'advance.payment',
        'pay_reconcile_id',
        string=u'预收单行',
        readonly=True,
        states={'draft': [('readonly', False)]},
        help=u'业务伙伴有预付款单,自动带出,用来与应收/应付款单核销')
    receivable_source_ids = fields.One2many(
        'source.order.line',
        'receivable_reconcile_id',
        string=u'应收结算单行',
        readonly=True,
        states={'draft': [('readonly', False)]},
        help=u'业务伙伴有应收结算单,自动带出,待与预收款单核销')
    payable_source_ids = fields.One2many(
        'source.order.line',
        'payable_reconcile_id',
        string=u'应付结算单行',
        readonly=True,
        states={'draft': [('readonly', False)]},
        help=u'业务伙伴有应付结算单,自动带出,待与预付款单核销')
    business_type = fields.Selection(TYPE_SELECTION,
                                     string=u'业务类型',
                                     readonly=True,
                                     states={'draft': [('readonly', False)]},
                                     help=u'类型:预收冲应收,预付冲应付,应收冲应付,应收转应收,应付转应付')
    name = fields.Char(string=u'单据编号',
                       copy=False,
                       readonly=True,
                       default='/',
                       help=u'单据编号,创建时会自动生成')
    date = fields.Date(string=u'单据日期',
                       readonly=True,
                       default=lambda self: fields.Date.context_today(self),
                       states={'draft': [('readonly', False)]},
                       help=u'单据创建日期')
    note = fields.Text(string=u'备注', help=u'可以为该单据添加一些需要的标识信息')

    @api.multi
    def _get_money_order(self, way='get'):
        money_orders = self.env['money.order'].search([
            ('partner_id', '=', self.partner_id.id), ('type', '=', way),
            ('state', '=', 'done'), ('to_reconcile', '!=', 0)
        ])
        result = []
        for order in money_orders:
            result.append((0, 0, {
                'name': order.id,
                'amount': order.amount,
                'date': order.date,
                'reconciled': order.reconciled,
                'to_reconcile': order.to_reconcile,
                'this_reconcile': order.to_reconcile,
            }))
        return result

    @api.multi
    def _get_money_invoice(self, way='income'):
        money_invoice = self.env['money.invoice'].search([
            ('category_id.type', '=', way),
            ('partner_id', '=', self.partner_id.id), ('to_reconcile', '!=', 0)
        ])
        result = []
        for invoice in money_invoice:
            result.append((0, 0, {
                'name': invoice.id,
                'category_id': invoice.category_id.id,
                'amount': invoice.amount,
                'date': invoice.date,
                'reconciled': invoice.reconciled,
                'to_reconcile': invoice.to_reconcile,
                'date_due': invoice.date_due,
                'this_reconcile': invoice.to_reconcile,
            }))
        return result

    @api.onchange('partner_id', 'to_partner_id', 'business_type')
    def onchange_partner_id(self):
        if not self.partner_id or not self.business_type:
            return {}

        # 先清空之前填充的数据
        self.advance_payment_ids = None
        self.receivable_source_ids = None
        self.payable_source_ids = None

        money_order = self.env['money.order']
        money_invoice = self.env['money.invoice']

        if self.business_type == 'adv_pay_to_get':  # 预收冲应收
            self.advance_payment_ids = self._get_money_order('get')
            self.receivable_source_ids = self._get_money_invoice('income')

        if self.business_type == 'adv_get_to_pay':  # 预付冲应付
            self.advance_payment_ids = self._get_money_order('pay')
            self.payable_source_ids = self._get_money_invoice('expense')

        if self.business_type == 'get_to_pay':  # 应收冲应付
            self.receivable_source_ids = self._get_money_invoice('income')
            self.payable_source_ids = self._get_money_invoice('expense')

        if self.business_type == 'get_to_get':  # 应收转应收
            self.receivable_source_ids = self._get_money_invoice('income')

        if self.business_type == 'pay_to_pay':  # 应付转应付
            self.payable_source_ids = self._get_money_invoice('expense')

    @api.multi
    def _get_or_pay(self, line, business_type, partner_id, to_partner_id,
                    name):
        if line.this_reconcile > line.to_reconcile:
            raise except_orm(u'错误', u'核销金额不能大于未核销金额')
        # 更新每一行的已核销余额、未核销余额
        line.name.to_reconcile -= line.this_reconcile
        line.name.reconciled += line.this_reconcile

        # 应收转应收、应付转应付
        if business_type in ['get_to_get', 'pay_to_pay']:
            self.env['money.invoice'].create({
                'name': name,
                'category_id': line.category_id.id,
                'amount': line.this_reconcile,
                'date': line.date,
                'reconciled': 0,  # 已核销金额
                'to_reconcile': line.this_reconcile,  # 未核销金额
                'date_due': line.date_due,
                'partner_id': to_partner_id.id,
            })

            if business_type == 'get_to_get':
                to_partner_id.receivable += line.this_reconcile
                partner_id.receivable -= line.this_reconcile
            if business_type == 'pay_to_pay':
                to_partner_id.payable += line.this_reconcile
                partner_id.payable -= line.this_reconcile

        return True

    @api.multi
    def reconcile_order_done(self):
        '''核销单的审核按钮'''
        # 核销金额不能大于未核销金额
        for order in self:
            if order.state == 'done':
                continue
            order_reconcile, invoice_reconcile = 0, 0
            if (self.business_type in ['get_to_get', 'pay_to_pay']
                    and order.partner_id.id == order.to_partner_id.id):
                raise except_orm(u'错误', u'转出客户和转入客户不能相同')

            # 核销预收预付
            for line in order.advance_payment_ids:
                order_reconcile += line.this_reconcile
                if line.this_reconcile > line.to_reconcile:
                    raise except_orm(u'错误', u'核销金额不能大于未核销金额')

                # 更新每一行的已核销余额、未核销余额
                line.name.to_reconcile -= line.this_reconcile
                line.name.reconciled += line.this_reconcile

            for line in order.receivable_source_ids:
                invoice_reconcile += line.this_reconcile
                self._get_or_pay(line, order.business_type, order.partner_id,
                                 order.to_partner_id, order.name)
            for line in order.payable_source_ids:
                if self.business_type == 'adv_get_to_pay':
                    invoice_reconcile += line.this_reconcile
                else:
                    order_reconcile += line.this_reconcile
                self._get_or_pay(line, order.business_type, order.partner_id,
                                 order.to_partner_id, order.name)

            # 核销金额必须相同
            if self.business_type in [
                    'adv_pay_to_get', 'adv_get_to_pay', 'get_to_pay'
            ]:
                if order_reconcile != invoice_reconcile:
                    raise except_orm(
                        u'错误', u'核销金额必须相同, %s 不等于 %s' %
                        (order_reconcile, invoice_reconcile))

            order.state = 'done'
        return True
Example #7
0
class PurchaseOrderLine(models.Model):
    _name = 'purchase.order.line'
    _description = 'Purchase Order Line'

    @api.depends('product_qty', 'price_unit', 'taxes_id')
    def _compute_amount(self):
        for line in self:
            taxes = line.taxes_id.compute_all(line.price_unit,
                                              line.order_id.currency_id,
                                              line.product_qty,
                                              product=line.product_id,
                                              partner=line.order_id.partner_id)
            line.update({
                'price_tax':
                taxes['total_included'] - taxes['total_excluded'],
                'price_total':
                taxes['total_included'],
                'price_subtotal':
                taxes['total_excluded'],
            })

    @api.depends('invoice_lines.invoice_id.state')
    def _compute_qty_invoiced(self):
        for line in self:
            qty = 0.0
            for inv_line in line.invoice_lines:
                qty += inv_line.uom_id._compute_qty_obj(
                    inv_line.uom_id, inv_line.quantity, line.product_uom)
            line.qty_invoiced = qty

    @api.depends('order_id.state', 'move_ids.state')
    def _compute_qty_received(self):
        for line in self:
            if line.order_id.state not in ['purchase', 'done']:
                line.qty_received = 0.0
                continue
            if line.product_id.type not in ['consu', 'product']:
                line.qty_received = line.product_qty
                continue
            total = 0.0
            for move in line.move_ids:
                if move.state == 'done':
                    total += move.product_qty
            line.qty_received = total

    name = fields.Text(string='Description', required=True)
    product_qty = fields.Float(
        string='Quantity',
        digits=dp.get_precision('Product Unit of Measure'),
        required=True,
        default=1.0)
    date_planned = fields.Datetime(string='Scheduled Date',
                                   required=True,
                                   select=True)
    taxes_id = fields.Many2many('account.tax', string='Taxes')
    product_uom = fields.Many2one('product.uom',
                                  string='Product Unit of Measure',
                                  required=True)
    product_id = fields.Many2one('product.product',
                                 string='Product',
                                 domain=[('purchase_ok', '=', True)],
                                 change_default=True,
                                 required=True)
    move_ids = fields.One2many('stock.move',
                               'purchase_line_id',
                               string='Reservation',
                               readonly=True,
                               ondelete='set null',
                               copy=False)
    price_unit = fields.Float(string='Unit Price',
                              required=True,
                              digits=dp.get_precision('Product Price'))

    price_subtotal = fields.Monetary(compute='_compute_amount',
                                     string='Subtotal',
                                     store=True)
    price_total = fields.Monetary(compute='_compute_amount',
                                  string='Total',
                                  store=True)
    price_tax = fields.Monetary(compute='_compute_amount',
                                string='Tax',
                                store=True)

    order_id = fields.Many2one('purchase.order',
                               string='Order Reference',
                               select=True,
                               required=True,
                               ondelete='cascade')
    account_analytic_id = fields.Many2one('account.analytic.account',
                                          string='Analytic Account')
    company_id = fields.Many2one('res.company',
                                 related='order_id.company_id',
                                 string='Company',
                                 store=True,
                                 readonly=True)
    state = fields.Selection(related='order_id.state', stored=True)

    invoice_lines = fields.One2many('account.invoice.line',
                                    'purchase_line_id',
                                    string="Invoice Lines",
                                    readonly=True,
                                    copy=False)

    # Replace by invoiced Qty
    qty_invoiced = fields.Float(compute='_compute_qty_invoiced',
                                string="Billed Qty",
                                store=True)
    qty_received = fields.Float(compute='_compute_qty_received',
                                string="Received Qty",
                                store=True)

    partner_id = fields.Many2one('res.partner',
                                 related='order_id.partner_id',
                                 string='Partner',
                                 readonly=True,
                                 store=True)
    currency_id = fields.Many2one(related='order_id.currency_id',
                                  store=True,
                                  string='Currency',
                                  readonly=True)
    date_order = fields.Datetime(related='order_id.date_order',
                                 string='Order Date',
                                 readonly=True)
    procurement_ids = fields.One2many('procurement.order',
                                      'purchase_line_id',
                                      string='Associated Procurements',
                                      copy=False)

    @api.multi
    def _create_stock_moves(self, picking):
        moves = self.env['stock.move']
        done = self.env['stock.move'].browse()
        for line in self:
            order = line.order_id
            price_unit = line.price_unit
            if line.taxes_id:
                price_unit = line.taxes_id.compute_all(
                    price_unit,
                    currency=line.order_id.currency_id,
                    quantity=1.0)['total_excluded']
            if line.product_uom.id != line.product_id.uom_id.id:
                price_unit *= line.product_uom.factor / line.product_id.uom_id.factor
            if order.currency_id != order.company_id.currency_id:
                price_unit = order.currency_id.compute(
                    price_unit, order.company_id.currency_id, round=False)

            template = {
                'name':
                line.name or '',
                'product_id':
                line.product_id.id,
                'product_uom':
                line.product_uom.id,
                'date':
                line.order_id.date_order,
                'date_expected':
                line.date_planned,
                'location_id':
                line.order_id.partner_id.property_stock_supplier.id,
                'location_dest_id':
                line.order_id._get_destination_location(),
                'picking_id':
                picking.id,
                'partner_id':
                line.order_id.dest_address_id.id,
                'move_dest_id':
                False,
                'state':
                'draft',
                'purchase_line_id':
                line.id,
                'company_id':
                line.order_id.company_id.id,
                'price_unit':
                price_unit,
                'picking_type_id':
                line.order_id.picking_type_id.id,
                'group_id':
                line.order_id.group_id.id,
                'procurement_id':
                False,
                'origin':
                line.order_id.name,
                'route_ids':
                line.order_id.picking_type_id.warehouse_id and [(6, 0, [
                    x.id for x in
                    line.order_id.picking_type_id.warehouse_id.route_ids
                ])] or [],
                'warehouse_id':
                line.order_id.picking_type_id.warehouse_id.id,
            }

            # Fullfill all related procurements with this po line
            diff_quantity = line.product_qty
            for procurement in line.procurement_ids:
                procurement_qty = procurement.product_uom._compute_qty_obj(
                    procurement.product_uom, procurement.product_qty,
                    line.product_uom)
                tmp = template.copy()
                tmp.update({
                    'product_uom_qty':
                    min(procurement_qty, diff_quantity),
                    'move_dest_id':
                    procurement.move_dest_id.
                    id,  #move destination is same as procurement destination
                    'procurement_id':
                    procurement.id,
                    'propagate':
                    procurement.rule_id.propagate,
                })
                done += moves.create(tmp)
                diff_quantity -= min(procurement_qty, diff_quantity)
            if float_compare(diff_quantity,
                             0.0,
                             precision_rounding=line.product_uom.rounding) > 0:
                template['product_uom_qty'] = diff_quantity
                done += moves.create(template)
        return done

    @api.multi
    def unlink(self):
        for line in self:
            if line.order_id.state in ['approved', 'done']:
                raise UserError(
                    _('Cannot delete a purchase order line which is in state \'%s\'.'
                      ) % (line.state, ))
            line.procurement_ids.message_post(
                body=_('Purchase order line deleted.'))
            line.procurement_ids.write({'state': 'exception'})
        return super(PurchaseOrderLine, self).unlink()

    @api.model
    def _get_date_planned(self, seller, po=False):
        """Return the datetime value to use as Schedule Date (``date_planned``) for
           PO Lines that correspond to the given product.seller_ids,
           when ordered at `date_order_str`.

           :param browse_record | False product: product.product, used to
               determine delivery delay thanks to the selected seller field (if False, default delay = 0)
           :param browse_record | False po: purchase.order, necessary only if
               the PO line is not yet attached to a PO.
           :rtype: datetime
           :return: desired Schedule Date for the PO line
        """
        date_order = po.date_order if po else self.order_id.date_order
        if date_order:
            return datetime.strptime(
                date_order, DEFAULT_SERVER_DATETIME_FORMAT) + relativedelta(
                    days=seller.delay if seller else 0)
        else:
            return datetime.today() + relativedelta(
                days=seller.delay if seller else 0)

    @api.onchange('product_id', 'product_qty', 'product_uom')
    def onchange_product_id(self):
        result = {}
        if not self.product_id:
            return result

        if self.product_id.uom_id.category_id.id != self.product_uom.category_id.id:
            self.product_uom = self.product_id.uom_po_id
        result['domain'] = {
            'product_uom':
            [('category_id', '=', self.product_id.uom_id.category_id.id)]
        }

        product_lang = self.product_id.with_context({
            'lang':
            self.partner_id.lang,
            'partner_id':
            self.partner_id.id,
        })
        self.name = product_lang.display_name
        if product_lang.description_purchase:
            self.name += '\n' + product_lang.description_purchase

        seller = self.product_id._select_seller(
            self.product_id,
            partner_id=self.partner_id,
            quantity=self.product_qty,
            date=self.order_id.date_order and self.order_id.date_order[:10],
            uom_id=self.product_uom)

        if seller or not self.date_planned:
            self.date_planned = self._get_date_planned(seller).strftime(
                DEFAULT_SERVER_DATETIME_FORMAT)

        if not seller:
            return result

        fpos = self.order_id.fiscal_position_id
        self.taxes_id = fpos.map_tax(self.product_id.supplier_taxes_id)

        price_unit = self.env['account.tax']._fix_tax_included_price(
            seller.price, self.product_id.supplier_taxes_id,
            self.taxes_id) if seller else 0.0
        if price_unit and seller and self.order_id.currency_id and seller.currency_id != self.order_id.currency_id:
            price_unit = seller.currency_id.compute(price_unit,
                                                    self.order_id.currency_id)
        self.price_unit = price_unit

        return result
Example #8
0
class HtmlForm(models.Model):

    _name = "html.form"
    _description = "HTML Form"
    
    def _default_return_url(self):
        return request.httprequest.host_url + "form/thankyou"

    def _default_submit_url(self):
        return request.httprequest.host_url + "form/insert"
    
    name = fields.Char(string="Form Name", required=True)
    model_id = fields.Many2one('ir.model', string="Model", required=True)
    fields_ids = fields.One2many('html.form.field', 'html_id', string="HTML Fields")
    output_html = fields.Text(string='Embed Code', readonly=True)
    required_fields = fields.Text(readonly=True, string="Required Fields")
    defaults_values = fields.One2many('html.form.defaults', 'html_id', string="Default Values", help="Sets the value of an field before it gets inserted into the database")
    return_url = fields.Char(string="Return URL", default=_default_return_url, help="The URL that the user will be redirected to after submitting the form", required=True)
    submit_url = fields.Char(string="Submit URL", default=_default_submit_url)
        
    @api.onchange('model_id')
    def _onchange_model_id(self):
        #delete all existing fields
        for field_entry in self.fields_ids:
            field_entry.unlink()
        
        required_string = ""
        for model_field in self.env['ir.model.fields'].search([('model_id','=', self.model_id.id),('required','=',True) ]):
            required_string += model_field.field_description.encode("utf-8") + " (" + model_field.name.encode("utf-8") + ")\n"
        
        self.required_fields = required_string
    
    @api.one
    def generate_form(self):
        html_output = ""
        html_output += "<form method=\"POST\" action=\"" + self.submit_url + "\" enctype=\"multipart/form-data\">\n"
        html_output += "  <h1>" + self.name.encode("utf-8") + "</h1>\n"
                                 
        for fe in self.fields_ids:
            
            #each field type has it's own function that way we can make plugin modules with new field types
            method = '_generate_html_%s' % (fe.field_type.html_type,)
            action = getattr(self, method, None)
        
            if not action:
	        raise NotImplementedError('Method %r is not implemented on %r object.' % (method, self))
            
            html_output += action(fe)
 
	html_output += "  <input type=\"hidden\" name=\"form_id\" value=\"" + str(self.id) + "\"/>\n"
        html_output += "  <input type=\"submit\" value=\"Send\"/>\n"
    	html_output += "</form>\n"
        self.output_html = html_output

    def _generate_html_file_select(self, fe):
        html_output = ""
	html_output += "  <label for='" + fe.html_name.encode("utf-8") + "'>" + fe.field_label + "</label>\n"
		    		
	html_output += "  <input type=\"file\" id=\"" + fe.html_name.encode("utf-8") + "\" name=\"" + fe.html_name.encode("utf-8") + "\""

	if fe.field_id.required == True:
	    html_output += " required"
	
	html_output += "/><br/>\n"
	
	return html_output

    def _generate_html_textbox(self, fe):
        html_output = ""
        
        html_output += "  <label for='" + fe.html_name.encode("utf-8") + "'>" + fe.field_label + "</label>\n"
		    		
	html_output += "  <input type=\"text\" id=\"" + fe.html_name.encode("utf-8") + "\" name=\"" + fe.html_name.encode("utf-8") + "\""
		                                    
	if fe.field_id.required == True:
	    html_output += " required"
	
	html_output += "/><br/>\n"
	
	return html_output
	
    def _generate_html_textarea(self, fe):
        html_output = ""
	html_output += "  <label for='" + fe.html_name.encode("utf-8") + "'>" + fe.field_label + "</label>"
		    		
	html_output += "  <textarea id=\"" + fe.html_name.encode("utf-8") + "\" name=\"" + fe.html_name.encode("utf-8") + "\""
		                                    
	if fe.field_id.required == True:
	    html_output += " required"
	
	html_output += "/><br/>\n"
	
	return html_output
class answer(models.Model):
    _inherit = "crm_profiling.answer"

    text = fields.Text("Open Answer", translate=True)
Example #10
0
 stylesheet_id = fields.Many2one('report.stylesheets',
                                 'Template Stylesheet')
 preload_mode = fields.Selection([
     ('static', _('Static')),
     ('preload', _('Preload')),
 ],
                                 string='Preload Mode')
 tml_source = fields.Selection([
     ('database', 'Database'),
     ('file', 'File'),
     ('parser', 'Parser'),
 ],
                               string='Template source',
                               default='database',
                               select=True)
 parser_def = fields.Text('Parser Definition')
 parser_loc = fields.Char(
     'Parser location',
     size=128,
     help="Path to the parser location. Beginning of the path must be start \
           with the module name!\n Like this: {module name}/{path to the \
           parser.py file}")
 parser_state = fields.Selection([
     ('default', _('Default')),
     ('def', _('Definition')),
     ('loc', _('Location')),
 ],
                                 'State of Parser',
                                 select=True)
 report_type = fields.Selection(selection_add=[('aeroo', 'Aeroo Reports')])
 process_sep = fields.Boolean(
Example #11
0
class Partner(models.Model):

    _inherit = 'res.partner'

    _order = 'write_date desc'

    # ----------------------------------------------------------- Deprecated ------------------------------------------------------
    #x_quotation_ids = fields.One2many(
    #		'openhealth.quotation',
    #		'partner_id',
    #		string="Cotizacion"
    #	)

    # ----------------------------------------------------------- Indexed ------------------------------------------------------
    x_dni = fields.Char(
        "DNI",
        index=True,
        required=False,
    )

    name = fields.Char(
        'Name',
        select=True,
        index=True,
    )

    # ----------------------------------------------------------- QC ------------------------------------------------------

    x_completeness = fields.Integer(string="Completeness", )

    # ----------------------------------------------------------- Code ------------------------------------------------------

    x_id_code = fields.Char(

        #'Patient ID',
        'Nr Historia Médica',
        size=256,

        #default='55',
        #default=_get_default_team
        #default=_get_default_id_code,

        #help='Patient Identifier provided by the Health Center',

        #readonly=True,
        readonly=False,
    )

    # ----------------------------------------------------------- My Company ------------------------------------------------------

    # Series
    x_series = fields.Char(string='Serie', )

    # Autorization
    x_authorization = fields.Char(string='Autorización', )

    # Warning Sales
    x_warning = fields.Text('Condiciones de Venta', )

    # Warning Purchase
    x_warning_purchase = fields.Text('Condiciones de Compra', )

    # ----------------------------------------------------------- Lang ------------------------------------------------------
    @api.model
    def _lang_get(self):
        languages = self.env['res.lang'].search([])
        return [(language.code, language.name) for language in languages]

    lang = fields.Selection(
        _lang_get,
        'Language',
        help="",
    )

    # ----------------------------------------------------------- Hard wired ------------------------------------------------------

    # READY
    phone = fields.Char(
        #'Teléfono 1',
        'Fijo',
        #required=True,
        required=False,
    )

    mobile = fields.Char('Celular', )

    x_firm = fields.Char("Razón social", )

    x_ruc = fields.Char("RUC", )

    email = fields.Char(
        string='Email',
        placeholder='',

        #required=True,
        required=False,
    )

    # Address

    country_id = fields.Many2one(
        'res.country',
        string='País',
        default=175,  # Peru

        #ondelete='restrict',
        required=True,
    )

    city = fields.Selection(
        selection=pat_vars._city_list,
        string='Departamento',
        default='lima',

        #required=True,
        required=False,
    )

    # For patient short card
    city_char = fields.Char(compute='_compute_city_char', )
    #@api.multi
    @api.depends('city')
    def _compute_city_char(self):
        for record in self:
            record.city_char = record.city

    street2 = fields.Char(
        string="Distrito 2",

        #required=True,
        required=False,
    )

    street2_sel = fields.Selection(
        selection=pat_vars._street2_list,
        string="Distrito",

        #required=True,
        required=False,
    )

    street = fields.Char(
        string="Dirección",
        required=False,
    )

    zip = fields.Char(
        #'Zip',
        string='Código',
        size=24,
        #change_default=True,
        required=False,
    )

    # Only for foreign addresses

    x_foreign = fields.Boolean(string="Dirección en el extranjero", )

    @api.onchange('x_foreign')
    def _onchange_x_foreign(self):

        print 'jx'
        print 'On change foreign'

        if self.x_foreign:
            #self.city = False
            self.city = ""

    x_address_foreign = fields.Text(string=".", )

    # ----------------------------------------------------------- DNI RUC ------------------------------------------------------

    # Test and validate
    @api.onchange('x_dni')
    def _onchange_x_dni(self):
        ret = pat_funcs.test_for_digits(self, self.x_dni)
        if ret != 0:
            return ret
        ret = pat_funcs.test_for_length(self, self.x_dni, 8)
        if ret != 0:
            return ret

    # Test and validate
    @api.onchange('x_ruc')
    def _onchange_x_ruc(self):
        ret = pat_funcs.test_for_digits(self, self.x_ruc)
        if ret != 0:
            return ret

        ret = pat_funcs.test_for_length(self, self.x_ruc, 11)
        if ret != 0:
            return ret

# ----------------------------------------------------------- On Changes ------------------------------------------------------
# Name

    @api.onchange('name')
    def _onchange_name(self):
        print 'jx'
        print 'Change name'

        if self.name != False:
            print self.name

            #self.name = self.name.strip().upper()
            name = self.name
            name = name.strip().upper()
            name = " ".join(name.split())
            self.name = name

            print self.name
            print

    # Address
    x_address = fields.Char(
        "Dirección",
        compute='_compute_x_address',
    )

    @api.multi
    #@api.depends('')
    def _compute_x_address(self):
        for record in self:

            #com = record.order.x_my_company
            if record.street != False and record.street2 != False and record.city != False:

                #record.x_address = record.street + ' - ' + record.street2 + ' - ' + record.city
                #record.x_address = record.street.title() + ' - ' + record.street2.title() + ' - ' + record.city.title()
                record.x_address = record.street.title(
                ) + ' ' + record.street2.title() + ' - ' + record.city.title()

    # Commons
    vspace = fields.Char(' ', readonly=True)

    x_my_company = fields.Boolean('Mi compañía ?', )

    #'state_id': fields.many2one("res.country.state", 'State', ondelete='restrict'),
    #'zip': fields.char('Zip', size=24, change_default=True),

    #lang = fields.Selection(
    #	_lang_get,
    #	'Language',
    #	default='es_ES',
    #	help="If the selected language is loaded in the system, all documents related to this contact will be printed in this language. If not, it will be English."
    #)

    # Function
    function = fields.Selection([
        ('reception', 'Plataforma'),
        ('cash', 'Caja'),
        ('assistant', 'Asistente Medico'),
        ('physician', 'Medico'),
        ('inventory', 'Almacen'),
        ('hc', 'Personal'),
        ('marketing', 'Marketing'),
        ('accounting', 'Contabilidad'),
        ('manager', 'Gerente'),
        ('lawyer', 'Abogado'),
    ], )

    # Pricelist
    #property_product_pricelist = fields.Many2one(
    #		'product.pricelist',
    #		'Sale Pricelist - jx',
    #		#compute='_compute_ppl',
    #	)

    #@api.multi
    #@api.depends('x_card')
    #def _compute_ppl(self):
    #	print 'jx'
    #	print 'Compute PPL'
    #	for record in self:
    #		record.property_product_pricelist = False

    # Vip
    x_vip = fields.Boolean(
        string="VIP",
        default=False,
        compute='_compute_x_vip',
    )

    @api.multi
    #@api.depends('x_card')
    def _compute_x_vip(self):

        #print 'jx'
        #print 'Compute Partner x Vip'

        for record in self:

            x_card = record.env['openhealth.card'].search(
                [
                    ('patient_name', '=', record.name),
                ],
                #order='appointment_date desc',
                limit=1,
            )

            if x_card.name != False:
                record.x_vip = True
                #pricelist_name = 'VIP'

                record.action_ppl_vip()

            else:
                record.x_vip = False
                #pricelist_name = 'Public Pricelist'

                record.action_ppl_public()

            # Pricelist
            #pricelist = self.env['product.pricelist'].search([
            #															('name','=', pricelist_name),
            #													],
            #order='appointment_date desc',
            #												limit=1,)
            #print pricelist
            #print record.property_product_pricelist
            #record.property_product_pricelist = pricelist
            #print record.property_product_pricelist

# ----------------------------------------------------------- Actions ------------------------------------------------------

# PPL

    @api.multi
    def action_ppl_public(self):

        print 'jx'
        print 'PPL Public'

        pricelist_name = 'Public Pricelist'

        # Pricelist
        pricelist = self.env['product.pricelist'].search(
            [
                ('name', '=', pricelist_name),
            ],
            #order='appointment_date desc',
            limit=1,
        )

        self.property_product_pricelist = pricelist

    # PPL
    @api.multi
    def action_ppl_vip(self):

        pricelist_name = 'VIP'

        # Pricelist
        pricelist = self.env['product.pricelist'].search(
            [
                ('name', '=', pricelist_name),
            ],
            #order='appointment_date desc',
            limit=1,
        )

        self.property_product_pricelist = pricelist

    # Removem
    @api.multi
    def remove_myself(self):

        #self.street = 'a'
        #self.x_dni = 'a'
        #self.email = 'a'
        #self.phone = 'a'

        self.sale_order_ids.unlink()

        self.invoice_ids.unlink()

        #self.purchase_order_count = 0

        self.unlink()

    x_autofill = fields.Boolean(
        string="Autofill",
        default=False,
    )

    @api.onchange('x_autofill')
    def _onchange_x_autofill(self):

        if self.x_autofill == True:

            self.street = 'x'
            self.street2 = 'x'
            self.city = 'x'
            self.country_id = 175
            self.x_dni = 'x'
            self.email = 'x'
            self.phone = 'x'

            for invoice in self.invoice_ids:
                invoice.state = 'draft'

# ----------------------------------------------------------- CRUD ------------------------------------------------------

# Create

    @api.model
    def create(self, vals):

        print
        print 'CRUD - Partner - Create'
        #print vals
        #print

        # Here
        key = 'name'
        if key in vals:

            # Compact Name
            #print vals[key]
            name = vals[key]
            name = name.strip().upper()
            name = " ".join(name.split())
            vals[key] = name
            #print vals[key]

        # Put your logic here
        res = super(Partner, self).create(vals)
        # Put your logic here

        # Create Patient
        #print 'Create Patient'
        #print name
        #print res
        #name = res.name
        #print name
        #patient = self.env['oeh.medical.patient'].create({
        #													'name': name,
        #	})
        #print patient
        #print patient.name

        return res

    # CRUD - Create


# Write

    @api.multi
    def write(self, vals):

        #print
        #print 'CRUD - Partner - Write'
        #print
        #print vals
        #print
        #print

        #if vals['name'] != False:
        #	vals['name'] = vals['name'].strip().upper()
        #	print vals['name']

        #Write your logic here
        res = super(Partner, self).write(vals)
        #Write your logic here

        #print self.name
        #name = self.name
        #self.name = self.name.upper()
        #self.name = name.upper()

        #print self.name
        #print

        return res
Example #12
0
class ubicacion(models.Model):
    _name = 'it_tools.ubicacion'

    name = fields.Char(string='NombreCorto', required=True)
    description = fields.Text()
Example #13
0
class DonationSourceFund(models.Model):
    _name = 'donation.sourcefund'
    _description = 'Code attributed for a Donation Source of Fund'
    _rec_name = 'display_name'

    @api.multi
    @api.depends('code', 'name')
    def _compute_display_name(self):
        for camp in self:
            name = camp.name
            if camp.code:
                name = u'[%s] %s' % (camp.code, name)
            camp.display_name = name

    @api.one
    @api.depends('organization')
    def _compute_max_code(self):
        if self.organization == 1:
            self.env.cr.execute(
                "SELECT count(id) FROM donation_sourcefund where organization = '%s'"
                % (self.organization))
            my_number = self.env.cr.fetchone()[0]
            code = my_number + 1
            code2 = '%03d' % my_number
            code3 = '0' + code2
            #vals['last_code'] = my_number
        elif self.organization == 2:
            self.env.cr.execute(
                "SELECT count(id) FROM donation_sourcefund where organization = '%s'"
                % (self.organization))
            my_number = self.env.cr.fetchone()[0]
            code = my_number + 1
            code2 = '%03d' % my_number
            code3 = '1' + code2
            #vals['last_code'] = my_number
        elif self.organization == 3:
            self.env.cr.execute(
                "SELECT count(id) FROM donation_sourcefund where organization = '%s'"
                % (self.organization))
            my_number = self.env.cr.fetchone()[0]
            code = my_number + 1
            code2 = '%03d' % my_number
            code3 = '2' + code2
            #vals['last_code'] = my_number
        elif self.organization == 4:
            self.env.cr.execute(
                "SELECT count(id) FROM donation_sourcefund where organization = '%s'"
                % (self.organization))
            my_number = self.env.cr.fetchone()[0]
            code = my_number + 1
            code2 = '%03d' % my_number
            code3 = '3' + code2
            #vals['last_code'] = my_number
        elif self.organization == 5:
            self.env.cr.execute(
                "SELECT count(id) FROM donation_sourcefund where organization = '%s'"
                % (self.organization))
            my_number = self.env.cr.fetchone()[0]
            code = my_number + 1
            code2 = '%03d' % my_number
            code3 = '4' + code2
            #vals['last_code'] = my_number
        elif self.organization == 6:
            self.env.cr.execute(
                "SELECT count(id) FROM donation_sourcefund where organization = '%s'"
                % (self.organization))
            my_number = self.env.cr.fetchone()[0]
            code = my_number + 1
            code2 = '%03d' % my_number
            code3 = '5' + code2
            #vals['last_code'] = my_number
        elif self.organization == 7:
            self.env.cr.execute(
                "SELECT count(id) FROM donation_sourcefund where organization = '%s'"
                % (self.organization))
            my_number = self.env.cr.fetchone()[0]
            code = my_number + 1
            code2 = '%03d' % my_number
            code3 = '6' + code2
            #vals['last_code'] = my_number
        elif self.organization == 8:
            self.env.cr.execute(
                "SELECT count(id) FROM donation_sourcefund where organization = '%s'"
                % (self.organization))
            my_number = self.env.cr.fetchone()[0]
            code = my_number + 1
            code2 = '%03d' % my_number
            code3 = '7' + code2
            #vals['last_code'] = my_number
        elif self.organization == 9:
            self.env.cr.execute(
                "SELECT count(id) FROM donation_sourcefund where organization = '%s'"
                % (self.organization))
            my_number = self.env.cr.fetchone()[0]
            code = my_number + 1
            code2 = '%03d' % my_number
            code3 = '8' + code2
            #vals['last_code'] = my_number
        elif self.organization == 10:
            self.env.cr.execute(
                "SELECT count(id) FROM donation_sourcefund where organization = '%s'"
                % (self.organization))
            my_number = self.env.cr.fetchone()[0]
            code = my_number + 1
            code2 = '%03d' % my_number
            code3 = '9' + code2
            #vals['last_code'] = my_number
        else:
            code3 = '0000'
            my_number = 0
            #vals['last_code'] = 0
        self.code = code3
        #return vals
        vals = {'last_code': my_number}
        return vals

    @api.depends('organization', 'max_code')
    def _compute_code(self):
        for source in self:
            code = ''
            if source.organization == 1:
                self.env.cr.execute(
                    "SELECT count(id) FROM donation_sourcefund where organization = '%s'"
                    % (source.organization))
                my_number = self.env.cr.fetchone()[0]
                if my_number > 0:
                    self.env.cr.execute(
                        "SELECT max(max_code2) FROM donation_sourcefund where organization='%s'"
                        % (source.organization))
                    max_code2 = self.env.cr.fetchone()[0]
                    if max_code2 > 0:
                        codee = max_code2 + 1
                        code3 = '%03d' % codee
                        code = '0' + code3
                    else:
                        code = '0001'
                else:
                    code = '0001'
            elif source.organization == 2:
                self.env.cr.execute(
                    "SELECT count(id) FROM donation_sourcefund where organization = '%s'"
                    % (source.organization))
                my_number = self.env.cr.fetchone()[0]
                if my_number > 0:
                    self.env.cr.execute(
                        "SELECT max(max_code2) FROM donation_sourcefund where organization='%s'"
                        % (source.organization))
                    max_code2 = self.env.cr.fetchone()[0]
                    if max_code2 > 0:
                        codee = max_code2 + 1
                        code3 = '%03d' % codee
                        code = '0' + code3
                    else:
                        code = '1001'
                else:
                    code = '1001'
            elif source.organization == 3:
                self.env.cr.execute(
                    "SELECT count(id) FROM donation_sourcefund where organization = '%s'"
                    % (source.organization))
                my_number = self.env.cr.fetchone()[0]
                if my_number > 0:
                    self.env.cr.execute(
                        "SELECT max(max_code2) FROM donation_sourcefund where organization='%s'"
                        % (source.organization))
                    max_code2 = self.env.cr.fetchone()[0]
                    if max_code2 > 0:
                        codee = max_code2 + 1
                        code3 = '%03d' % codee
                        code = '0' + code3
                    else:
                        code = '2001'
                else:
                    code = '2001'
            elif source.organization == 4:
                self.env.cr.execute(
                    "SELECT count(id) FROM donation_sourcefund where organization = '%s'"
                    % (source.organization))
                my_number = self.env.cr.fetchone()[0]
                if my_number > 0:
                    self.env.cr.execute(
                        "SELECT max(max_code2) FROM donation_sourcefund where organization='%s'"
                        % (source.organization))
                    max_code2 = self.env.cr.fetchone()[0]
                    if max_code2 > 0:
                        codee = max_code2 + 1
                        code3 = '%03d' % codee
                        code = '0' + code3
                    else:
                        code = '3001'
                else:
                    code = '3001'
            elif source.organization == 5:
                self.env.cr.execute(
                    "SELECT count(id) FROM donation_sourcefund where organization = '%s'"
                    % (source.organization))
                my_number = self.env.cr.fetchone()[0]
                if my_number > 0:
                    self.env.cr.execute(
                        "SELECT max(max_code2) FROM donation_sourcefund where organization='%s'"
                        % (source.organization))
                    max_code2 = self.env.cr.fetchone()[0]
                    if max_code2 > 0:
                        codee = max_code2 + 1
                        code3 = '%03d' % codee
                        code = '0' + code3
                    else:
                        code = '4001'
                else:
                    code = '4001'
            elif source.organization == 6:
                self.env.cr.execute(
                    "SELECT count(id) FROM donation_sourcefund where organization = '%s'"
                    % (source.organization))
                my_number = self.env.cr.fetchone()[0]
                if my_number > 0:
                    self.env.cr.execute(
                        "SELECT max(max_code2) FROM donation_sourcefund where organization='%s'"
                        % (source.organization))
                    max_code2 = self.env.cr.fetchone()[0]
                    if max_code2 > 0:
                        codee = max_code2 + 1
                        code3 = '%03d' % codee
                        code = '0' + code3
                    else:
                        code = '5001'
                else:
                    code = '5001'
            else:
                code = '0000'
            source.code = code

    def onchange_address_id(self, cr, uid, ids, address, context=None):
        if address:
            address = self.pool.get('res.partner').browse(cr,
                                                          uid,
                                                          address,
                                                          context=context)
            return {
                'value': {
                    'partner_phone': address.phone,
                    'mobile_phone': address.mobile,
                    'work_email': address.email,
                    'partner_website': address.website
                }
            }
        return {'value': {}}

    #@api.onchange('organization')
    #def onchange_org_id(self, cr, uid, ids, context=None):
    #   record = self.browse(cr, uid, ids)
    #  if self.organization:
    #     mynumber = 123
    #    return {'value': {'last_code': my_number}}
    #return {'value': {}}

    address_id = fields.Many2one('res.partner',
                                 string='Focal Person Of Contact')
    partner_phone = fields.Char('Focal Person Phone', readonly=False)
    mobile_phone = fields.Char('Focal Person Mobile', readonly=False)
    work_email = fields.Char('Focal Person Email', size=240)
    partner_website = fields.Char('Focal Person website', size=240)
    name = fields.Char(string='Name', required=True)
    code = fields.Char(string='Code', size=32)
    last_code = fields.Char(string='last code')
    organization = fields.Selection(selection=_ORGANIZATION, string='Type')
    display_name = fields.Char(string='Display Name',
                               compute='_compute_display_name',
                               readonly=True,
                               store=True)
    fundstream_id = fields.Many2one('donation.fundstream',
                                    string='Fund Stream',
                                    track_visibility='onchange',
                                    ondelete='restrict')
    start_date = fields.Date(string='Start Date',
                             default=fields.Date.context_today)
    nota = fields.Text(string='Notes')
    responsible = fields.Many2one('res.users',
                                  'Responsibility',
                                  track_visibility='onchange')
    total_amount = fields.Float(string='Total', readonly=True)
    _sql_constraints = [('display_name_uniq', 'UNIQUE (name)',
                         'This Fund-stream already exists'),
                        ('code_uniq', 'UNIQUE (code)',
                         'This Fund-stream Code already exists')]

    @api.model
    def create(self, vals):
        return super(DonationSourceFund, self).create(vals)
Example #14
0
class OpFaculty(models.Model):
    _name = 'op.faculty'
    _inherits = {'res.partner': 'partner_id'}

    partner_id = fields.Many2one('res.partner',
                                 'Partner',
                                 required=True,
                                 ondelete="cascade")
    middle_name = fields.Char('Middle Name', size=128)
    last_name = fields.Char('Last Name', size=128, required=True)
    birth_date = fields.Date('Birth Date', required=True)
    blood_group = fields.Selection([('A+', 'A+ve'), ('B+', 'B+ve'),
                                    ('O+', 'O+ve'), ('AB+', 'AB+ve'),
                                    ('A-', 'A-ve'), ('B-', 'B-ve'),
                                    ('O-', 'O-ve'), ('AB-', 'AB-ve')],
                                   'Blood Group')
    gender = fields.Selection([('male', 'Male'), ('female', 'Female')],
                              'Gender',
                              required=True)
    nationality = fields.Many2one('res.country', 'Nationality')
    emergency_contact = fields.Many2one('res.partner', 'Emergency Contact')
    visa_info = fields.Char('Visa Info', size=64)
    id_number = fields.Char('ID Card Number', size=64)
    photo = fields.Binary('Photo')
    login = fields.Char('Login',
                        related='partner_id.user_id.login',
                        readonly=1)
    last_login = fields.Datetime('Latest Connection',
                                 related='partner_id.user_id.login_date',
                                 readonly=1)
    subject_ids = fields.Many2many('op.subject', string='Subject(s)')
    faculty_subject_ids = fields.One2many('op.faculty.subject',
                                          'faculty_id',
                                          string="Faculty subject records")
    emp_id = fields.Many2one('hr.employee', 'Employee')
    campus_id = fields.Many2one('op.campus',
                                string='Campus',
                                size=128,
                                required=True)
    institute_id = fields.Many2one('op.institute',
                                   string='Institute',
                                   size=128,
                                   required=True)
    department_id = fields.Many2one('op.department',
                                    string='Department',
                                    size=128,
                                    required=True)
    experience = fields.Text(string="Experience")

    @api.one
    @api.constrains('birth_date')
    def _check_birthdate(self):
        if self.birth_date > fields.Date.today():
            raise ValidationError(
                _("Birth Date can't be greater than current date!"))

    @api.one
    def create_employee(self):
        vals = {
            'name':
            self.name + ' ' + (self.middle_name or '') + ' ' + self.last_name,
            'country_id': self.nationality.id,
            'gender': self.gender,
            'address_home_id': self.partner_id.id
        }
        emp_id = self.env['hr.employee'].create(vals)
        self.write({'emp_id': emp_id.id})
        self.partner_id.write({'supplier': True, 'employee': True})

    @api.multi
    def name_get(self):
        res = []
        for value in self:
            if value.middle_name:
                res.append([
                    value.id,
                    "%s %s %s" %
                    (value.name, value.middle_name, value.last_name)
                ])
            else:
                res.append([value.id, "%s %s" % (value.name, value.last_name)])
        return res
Example #15
0
class UserGuide(models.Model):
	_name="user.guide"


	def _name_get_resname(self, cr, uid, ids, object, method, context):
		data = {}
		for attachment in self.browse(cr, uid, ids, context=context):
			model_object = attachment.res_model
			res_id = attachment.res_id
			if model_object and res_id:
				model_pool = self.pool[model_object]
				res = model_pool.name_get(cr,uid,[res_id],context)
				res_name = res and res[0][1] or None
				if res_name:
					field = self._columns.get('res_name',False)
					if field and len(res_name) > field.size:
						res_name = res_name[:30] + '...' 
				data[attachment.id] = res_name or False
			else:
				data[attachment.id] = False
		return data

	def _storage(self, cr, uid, context=None):
		return self.pool['ir.config_parameter'].get_param(cr, SUPERUSER_ID, 'ir_attachment.location', 'file')

	def _filestore(self, cr, uid, context=None):
		return tools.config.filestore(cr.dbname)

	def force_storage(self, cr, uid, context=None):
		"""Force all attachments to be stored in the currently configured storage"""
		if not self.pool['res.users'].has_group(cr, uid, 'base.group_erp_manager'):
			raise AccessError(_('Only administrators can execute this action.'))

		location = self._storage(cr, uid, context)
		domain = {
			'db': [('store_fname', '!=', False)],
			'file': [('db_datas', '!=', False)],
		}[location]

		ids = self.search(cr, uid, domain, context=context)
		for attach in self.browse(cr, uid, ids, context=context):
			attach.write({'datas': attach.datas})
		return True

	# 'data' field implementation
	def _full_path(self, cr, uid, path):
		# sanitize ath
		path = re.sub('[.]', '', path)
		path = path.strip('/\\')
		return os.path.join(self._filestore(cr, uid), path)

	def _get_path(self, cr, uid, bin_data):
		sha = hashlib.sha1(bin_data).hexdigest()

		# retro compatibility
		fname = sha[:3] + '/' + sha
		full_path = self._full_path(cr, uid, fname)
		if os.path.isfile(full_path):
			return fname, full_path		# keep existing path

		# scatter files across 256 dirs
		# we use '/' in the db (even on windows)
		fname = sha[:2] + '/' + sha
		full_path = self._full_path(cr, uid, fname)
		dirname = os.path.dirname(full_path)
		if not os.path.isdir(dirname):
			os.makedirs(dirname)
		return fname, full_path

	def _file_read(self, cr, uid, fname, bin_size=False):
		full_path = self._full_path(cr, uid, fname)
		r = ''
		try:
			if bin_size:
				r = os.path.getsize(full_path)
			else:
				r = open(full_path,'rb').read().encode('base64')
		except IOError:
			_logger.exception("_read_file reading %s", full_path)
		return r

	def _file_write(self, cr, uid, value):
		bin_value = value.decode('base64')
		fname, full_path = self._get_path(cr, uid, bin_value)
		if not os.path.exists(full_path):
			try:
				with open(full_path, 'wb') as fp:
					fp.write(bin_value)
			except IOError:
				_logger.exception("_file_write writing %s", full_path)
		return fname

	def _file_delete(self, cr, uid, fname):
		# using SQL to include files hidden through unlink or due to record rules
		cr.execute("SELECT COUNT(*) FROM ir_attachment WHERE store_fname = %s", (fname,))
		count = cr.fetchone()[0]
		full_path = self._full_path(cr, uid, fname)
		if not count and os.path.exists(full_path):
			try:
				os.unlink(full_path)
			except OSError:
				_logger.exception("_file_delete could not unlink %s", full_path)
			except IOError:
				# Harmless and needed for race conditions
				_logger.exception("_file_delete could not unlink %s", full_path)

	def _data_get(self, cr, uid, ids, name, arg, context=None):
		if context is None:
			context = {}
		result = {}
		bin_size = context.get('bin_size')
		for attach in self.browse(cr, uid, ids, context=context):
			if attach.store_fname:
				result[attach.id] = self._file_read(cr, uid, attach.store_fname, bin_size)
			else:
				result[attach.id] = attach.db_datas
		return result

	def _data_set(self, cr, uid, id, name, value, arg, context=None):
		# We dont handle setting data to null
		if not value:
			return True
		if context is None:
			context = {}
		location = self._storage(cr, uid, context)
		file_size = len(value.decode('base64'))
		attach = self.browse(cr, uid, id, context=context)
		fname_to_delete = attach.store_fname
		if location != 'db':
			fname = self._file_write(cr, uid, value)
			# SUPERUSER_ID as probably don't have write access, trigger during create
			super(ir_attachment, self).write(cr, SUPERUSER_ID, [id], {'store_fname': fname, 'file_size': file_size, 'db_datas': False}, context=context)
		else:
			super(ir_attachment, self).write(cr, SUPERUSER_ID, [id], {'db_datas': value, 'file_size': file_size, 'store_fname': False}, context=context)

		# After de-referencing the file in the database, check whether we need
		# to garbage-collect it on the filesystem
		if fname_to_delete:
			self._file_delete(cr, uid, fname_to_delete)
		return True

	name = fields.Char("Name")
	department_id = fields.Many2one('hr.department',"Department")
	type_id = fields.Selection([('functional', 'Functional'), ('technical', 'Technical')])
	file_id = fields.Binary(_data_get, fnct_inv=_data_set, string='File Content', nodrop=True)
	description = fields.Text("Description")
class ResInvestConstruction(LogCommon, models.Model):
    _name = 'res.invest.construction'
    _inherit = ['res.invest.construction', 'mail.thread']

    code = fields.Char(
        readonly=True,
        default='/',
        copy=False,
        track_visibility='onchange',
    )
    state = fields.Selection(
        [
            ('draft', 'Draft'),
            ('submit', 'Submitted'),
            ('unapprove', 'Un-Approved'),
            ('approve', 'Approved'),
            ('reject', 'Rejected'),
            ('delete', 'Deleted'),
            ('cancel', 'Cancelled'),
            ('close', 'Closed'),
        ],
        string='Status',
        required=True,
        readonly=True,
        copy=False,
        default='draft',
        track_visibility='onchange',
    )
    month_duration = fields.Integer(
        string='Duration (months)',
        readonly=True,
        states={
            'draft': [('readonly', False)],
            'submit': [('readonly', False)],
            'unapprove': [('readonly', False)]
        },
        copy=False,
        track_visibility='onchange',
    )
    date_start = fields.Date(
        string='Start Date',
        readonly=True,
        states={
            'draft': [('readonly', False)],
            'submit': [('readonly', False)],
            'unapprove': [('readonly', False)]
        },
        copy=False,
        track_visibility='onchange',
    )
    date_end = fields.Date(
        string='End Date',
        readonly=True,
        states={
            'draft': [('readonly', False)],
            'submit': [('readonly', False)],
            'unapprove': [('readonly', False)]
        },
        copy=False,
        track_visibility='onchange',
    )
    pm_employee_id = fields.Many2one(
        'hr.employee',
        string='Project Manager',
        required=True,
        readonly=True,
        states={
            'draft': [('readonly', False)],
            'submit': [('readonly', False)]
        },
        track_visibility='onchange',
    )
    pm_section_id = fields.Many2one(
        'res.section',
        string='Project Manager Section',
        required=True,
        readonly=True,
        states={
            'draft': [('readonly', False)],
            'submit': [('readonly', False)]
        },
        track_visibility='onchange',
    )
    mission_id = fields.Many2one(
        'res.mission',
        string='Core Mission',
        required=True,
        readonly=True,
        states={
            'draft': [('readonly', False)],
            'submit': [('readonly', False)]
        },
        track_visibility='onchange',
    )
    amount_budget_plan = fields.Float(
        string='Planned Budget',
        compute='_compute_amount_budget_plan',
        readonly=True,
        track_visibility='onchange',
    )
    amount_budget = fields.Float(
        string='Approved Budget',
        default=0.0,
        readonly=True,
        states={
            'submit': [('readonly', False)],
            'unapprove': [('readonly', False)]
        },
        write=['pabi_base.group_cooperate_budget'],  # Only Corp can edit
        track_visibility='onchange',
    )
    amount_before = fields.Float(
        string='Before FY1',
        compute='_compute_amount_fy',
    )
    amount_fy1 = fields.Float(
        string='FY1',
        compute='_compute_amount_fy',
        help="FY1 means next fiscalyear, based on current date",
    )
    amount_fy2 = fields.Float(
        string='FY2',
        compute='_compute_amount_fy',
    )
    amount_fy3 = fields.Float(
        string='FY3',
        compute='_compute_amount_fy',
    )
    amount_fy4 = fields.Float(
        string='FY4',
        compute='_compute_amount_fy',
    )
    amount_beyond = fields.Float(
        string='FY5 and Beyond',
        compute='_compute_amount_fy',
    )
    amount_phase_approve = fields.Float(
        string='Approved Budget (Phases)',
        compute='_compute_amount_phase_approve',
        store=True,
    )
    operation_area = fields.Char(
        string='Operation Area',
        track_visibility='onchange',
    )
    date_expansion = fields.Date(
        string='Expansion Date',
        track_visibility='onchange',
    )
    approval_info = fields.Text(
        string='Approval Info',
        track_visibility='onchange',
    )
    project_readiness = fields.Text(
        string='Project Readiness',
        track_visibility='onchange',
    )
    reason = fields.Text(
        string='Reason',
        track_visibility='onchange',
    )
    expected_result = fields.Text(
        string='Expected Result',
        track_visibility='onchange',
    )
    budget_plan_ids = fields.One2many(
        'res.invest.construction.budget.plan',
        'invest_construction_id',
        string='Budget Planning',
    )
    phase_ids = fields.One2many(
        domain=['|', ('active', '=', True), ('active', '=', False)], )
    _sql_constraints = [
        ('number_uniq', 'unique(code)',
         'Constuction Project Code must be unique!'),
    ]

    @api.multi
    def _compute_amount_fy(self):
        Fiscal = self.env['account.fiscalyear']
        current_fy = Fiscal.browse(Fiscal.find())

        for rec in self:
            plans = rec.budget_plan_ids
            # Find current and previous years plan line
            prev_plans = plans.filtered(
                lambda l: l.fiscalyear_id.date_start <= current_fy.date_start)
            rec.amount_before = sum(prev_plans.mapped('amount_plan'))
            future_plans = plans - prev_plans  # Only future
            future_plans = future_plans.sorted(
                key=lambda l: l.fiscalyear_id.date_start)
            amount_beyond = 0.0
            years = len(future_plans)
            for i in range(0, years):
                if i < 4:  # only fy1 - fy4
                    rec['amount_fy%s' % (i + 1)] = future_plans[i].amount_plan
                else:
                    amount_beyond += future_plans[i].amount_plan
            rec.amount_beyond = amount_beyond

    @api.multi
    @api.constrains('budget_plan_ids')
    def _check_fiscalyear_unique(self):
        for rec in self:
            fiscalyear_ids = [x.fiscalyear_id.id for x in rec.budget_plan_ids]
            for x in fiscalyear_ids:
                if fiscalyear_ids.count(x) > 1:
                    raise ValidationError(_('Duplicate fiscalyear plan'))

    @api.model
    def _check_cooperate_access(self):
        if not self.env.user.has_group('pabi_base.group_cooperate_budget'):
            raise ValidationError(_('Only Cooperate Budget user is allowed!'))
        return True

    @api.multi
    @api.depends('phase_ids.amount_phase_approve')
    def _compute_amount_phase_approve(self):
        for rec in self:
            amount_total = sum([x.amount_phase_approve for x in rec.phase_ids])
            if amount_total and float_compare(
                    amount_total, rec.amount_budget, precision_digits=2) != 0:
                raise ValidationError(
                    _("Sum of all phases approved budget <> "
                      "Project's approved budget"))
            rec.amount_phase_approve = amount_total

    @api.multi
    @api.constrains('date_expansion', 'date_start', 'date_end')
    def _check_date(self):
        for rec in self:
            # Date End must >= Date Start
            if rec.date_end and rec.date_start and \
                    rec.date_end < rec.date_start:
                raise ValidationError(
                    _('End Date must start after Start Date!'))
            # Expansion Date must >= End date
            if rec.date_expansion and rec.date_end and \
                    rec.date_expansion < rec.date_end:
                raise ValidationError(
                    _('Expansion Date must start after End Date!'))

    @api.multi
    @api.depends('budget_plan_ids.amount_plan')
    def _compute_amount_budget_plan(self):
        for rec in self:
            rec.amount_budget_plan = \
                sum([x.amount_plan for x in rec.budget_plan_ids])

    @api.model
    def create(self, vals):
        if vals.get('code', '/') == '/':
            fiscalyear_id = self.env['account.fiscalyear'].find()
            vals['code'] = self.env['ir.sequence'].\
                with_context(fiscalyear_id=fiscalyear_id).\
                next_by_code('invest.construction')
        return super(ResInvestConstruction, self).create(vals)

    @api.onchange('pm_employee_id')
    def _onchange_user_id(self):
        employee = self.pm_employee_id
        self.pm_section_id = employee.section_id
        self.costcenter_id = employee.section_id.costcenter_id
        self.org_id = employee.org_id

    @api.onchange('month_duration', 'date_start', 'date_end')
    def _onchange_date(self):
        if not self.month_duration or not self.date_start:
            self.date_end = False
        else:
            date_start = datetime.strptime(self.date_start, '%Y-%m-%d').date()
            date_end = date_start + relativedelta(months=self.month_duration)
            self.date_end = date_end.strftime('%Y-%m-%d')
        self._prepare_budget_plan_line(self.date_start, self.date_end)

    @api.model
    def _prepare_budget_plan_line(self, date_start, date_end):
        self.budget_plan_ids = False
        Fiscal = self.env['account.fiscalyear']
        Plan = self.env['res.invest.construction.budget.plan']
        if date_start and date_end:
            fiscal_start_id = Fiscal.find(date_start)
            fiscal_end_id = Fiscal.find(date_end)
            fiscal_start = Fiscal.browse(fiscal_start_id)
            fiscal_end = Fiscal.browse(fiscal_end_id)
            if not fiscal_start.name.isdigit():
                raise ValidationError(
                    _("Config: Fiscalyear name not represent a year integer!"))
            fiscal_year = int(fiscal_start.name)
            while fiscal_year <= int(fiscal_end.name):
                fiscal = Fiscal.search([('name', '=', str(fiscal_year))])
                if fiscal:
                    plan = Plan.new()
                    plan.fiscalyear_id = fiscal
                    plan.amount_plan = 0.0
                    self.budget_plan_ids += plan
                fiscal_year += 1
        return True

    @api.multi
    def action_create_phase(self):
        for rec in self:
            if rec.phase_ids:
                continue
            phases = []
            i = 1
            for phase in sorted(CONSTRUCTION_PHASE.items()):
                phases.append((0, 0, {
                    'sequence': i,
                    'phase': phase[0],
                    'date_start': rec.date_start,
                    'fund_ids': [(6, 0, rec.fund_ids.ids)],
                }))
                i += 1
            rec.write({'phase_ids': phases})

    # Statuses
    @api.multi
    def action_submit(self):
        for rec in self:
            if not rec.amount_budget_plan:
                raise ValidationError(
                    _('Cannot submit project without planned amount.'))
        self.with_context(button_click=True).write({'state': 'submit'})

    @api.multi
    def action_approve(self):
        self._check_cooperate_access()
        self.action_create_phase()
        self.with_context(button_click=True).write({'state': 'approve'})

    @api.multi
    def action_unapprove(self):
        # Unapprove all phases, only those in Approved state
        for rec in self:
            rec.phase_ids.filtered(
                lambda l: l.state == 'approve').action_unapprove()
        self.with_context(button_click=True).write({'state': 'unapprove'})

    @api.multi
    def action_reject(self):
        self.with_context(button_click=True).write({'state': 'reject'})

    @api.multi
    def action_delete(self):
        self.with_context(button_click=True).write({'state': 'delete'})

    @api.multi
    def action_cancel(self):
        self.with_context(button_click=True).write({'state': 'cancel'})

    @api.multi
    def action_close(self):
        self.with_context(button_click=True).write({'state': 'close'})

    @api.multi
    def action_draft(self):
        self.with_context(button_click=True).write({'state': 'draft'})

    @api.multi
    def write(self, vals):
        # Only cooperate budget allowed to edit when state == 'submit'
        button_click = self._context.get('button_click', False)
        for rec in self:
            if not button_click and rec.state == 'submit':
                self._check_cooperate_access()
        return super(ResInvestConstruction, self).write(vals)
Example #17
0
class money_order(models.Model):
    _name = 'money.order'
    _description = u"收付款单"

    TYPE_SELECTION = [
        ('pay', u'付款'),
        ('get', u'收款'),
    ]

    @api.model
    def create(self, values):
        # 创建单据时,根据订单类型的不同,生成不同的单据编号
        if self._context.get('type') == 'pay':
            values.update({'name': self.env['ir.sequence'].get('pay.order')})
        else:
            values.update({'name': self.env['ir.sequence'].get('get.order')})

        # 创建时查找该业务伙伴是否存在 未审核 状态下的收付款单
        orders = self.env['money.order'].search([('partner_id', '=',
                                                  values.get('partner_id')),
                                                 ('state', '=', 'draft')])
        for order in orders:
            if order:
                raise except_orm(u'错误', u'该业务伙伴存在未审核的收/付款单,请先审核')

        return super(money_order, self).create(values)

    @api.multi
    def write(self, values):
        # 保存时查找该业务伙伴是否存在 未审核 状态下的收付款单
        if values.get('partner_id'):
            orders = self.env['money.order'].search([
                ('partner_id', '=', values.get('partner_id')),
                ('state', '=', 'draft')
            ])
            for order in orders:
                if order:
                    raise except_orm(u'错误', u'该业务伙伴存在未审核的收/付款单,请先审核')

        return super(money_order, self).write(values)

    @api.multi
    def unlink(self):
        for order in self:
            if order.state == 'done':
                raise except_orm(u'错误', u'不可以删除已经审核的单据')

        return super(money_order, self).unlink()

    @api.one
    @api.depends('discount_amount', 'line_ids.amount',
                 'source_ids.this_reconcile')
    def _compute_advance_payment(self):
        amount, this_reconcile = 0.0, 0.0
        for line in self.line_ids:
            amount += line.amount
        for line in self.source_ids:
            this_reconcile += line.this_reconcile
        self.advance_payment = amount - this_reconcile + self.discount_amount
        self.amount = amount

    @api.one
    @api.depends('partner_id')
    def _compute_currency_id(self):
        partner_currency_id = self.partner_id.c_category_id.account_id.currency_id.id or self.partner_id.s_category_id.account_id.currency_id.id
        self.currency_id = partner_currency_id or self.env.user.company_id.currency_id.id

    state = fields.Selection([
        ('draft', u'未审核'),
        ('done', u'已审核'),
    ],
                             string=u'状态',
                             readonly=True,
                             default='draft',
                             copy=False,
                             help=u'收付款单状态标识,新建时状态为未审核;审核后状态为已审核')
    partner_id = fields.Many2one('partner',
                                 string=u'业务伙伴',
                                 required=True,
                                 readonly=True,
                                 ondelete='restrict',
                                 states={'draft': [('readonly', False)]},
                                 help=u'该单据对应的业务伙伴,单据审核时会影响他的应收应付余额')
    date = fields.Date(string=u'单据日期',
                       readonly=True,
                       default=lambda self: fields.Date.context_today(self),
                       states={'draft': [('readonly', False)]},
                       help=u'单据创建日期')
    name = fields.Char(string=u'单据编号',
                       copy=False,
                       readonly=True,
                       help=u'单据编号,创建时会根据类型自动生成')
    note = fields.Text(string=u'备注', help=u'可以为该单据添加一些需要的标识信息')
    currency_id = fields.Many2one('res.currency',
                                  u'外币币别',
                                  compute='_compute_currency_id',
                                  store=True,
                                  readonly=True,
                                  help=u'业务伙伴的类别科目上对应的外币币别')
    discount_amount = fields.Float(string=u'整单折扣',
                                   readonly=True,
                                   states={'draft': [('readonly', False)]},
                                   digits=dp.get_precision('Amount'),
                                   help=u'本次折扣金额')
    discount_account_id = fields.Many2one('finance.account',
                                          u'折扣科目',
                                          help=u'收付款单审核生成凭证时,折扣额对应的科目')
    line_ids = fields.One2many('money.order.line',
                               'money_id',
                               string=u'收付款单行',
                               readonly=True,
                               states={'draft': [('readonly', False)]},
                               help=u'收付款单明细行')
    source_ids = fields.One2many('source.order.line',
                                 'money_id',
                                 string=u'结算单行',
                                 readonly=True,
                                 states={'draft': [('readonly', False)]},
                                 help=u'收付款单原始单据行')
    type = fields.Selection(TYPE_SELECTION,
                            string=u'类型',
                            default=lambda self: self._context.get('type'),
                            help=u'类型:收款单 或者 付款单')
    amount = fields.Float(string=u'总金额',
                          compute='_compute_advance_payment',
                          digits=dp.get_precision('Amount'),
                          store=True,
                          readonly=True,
                          help=u'收付款单行金额总和')
    advance_payment = fields.Float(string=u'本次预收/付款',
                                   compute='_compute_advance_payment',
                                   digits=dp.get_precision('Amount'),
                                   store=True,
                                   readonly=True,
                                   help=u'根据收付款单行金额总和,原始单据行金额总和及折扣额计算得来的预付款,'
                                   u'值>=0')
    to_reconcile = fields.Float(string=u'未核销预收款',
                                digits=dp.get_precision('Amount'),
                                help=u'未核销的预收/预付款金额')
    reconciled = fields.Float(string=u'已核销预收款',
                              digits=dp.get_precision('Amount'),
                              help=u'已核销的预收/预付款金额')
    origin_name = fields.Char(u'来源单号', help=u'来源单号')
    bank_name = fields.Char(u'开户行', help=u'开户行取自业务伙伴,可修改')
    bank_num = fields.Char(u'银行账号', help=u'银行账号取自业务伙伴,可修改')

    @api.onchange('date')
    def onchange_date(self):
        if self._context.get('type') == 'get':
            return {'domain': {'partner_id': [('c_category_id', '!=', False)]}}
        else:
            return {'domain': {'partner_id': [('s_category_id', '!=', False)]}}

    @api.onchange('partner_id')
    def onchange_partner_id(self):
        if not self.partner_id:
            return {}

        source_lines = []
        self.source_ids = []
        money_invoice = self.env['money.invoice']
        if self.env.context.get('type') == 'get':
            money_invoice = self.env['money.invoice'].search([
                ('partner_id', '=', self.partner_id.id),
                ('category_id.type', '=', 'income'), ('to_reconcile', '!=', 0)
            ])
        if self.env.context.get('type') == 'pay':
            money_invoice = self.env['money.invoice'].search([
                ('partner_id', '=', self.partner_id.id),
                ('category_id.type', '=', 'expense'), ('to_reconcile', '!=', 0)
            ])
            self.bank_name = self.partner_id.bank_name
            self.bank_num = self.partner_id.bank_num
        for invoice in money_invoice:
            source_lines.append({
                'name': invoice.id,
                'category_id': invoice.category_id.id,
                'amount': invoice.amount,
                'date': invoice.date,
                'reconciled': invoice.reconciled,
                'to_reconcile': invoice.to_reconcile,
                'this_reconcile': invoice.to_reconcile,
                'date_due': invoice.date_due,
            })
        self.source_ids = source_lines

    @api.multi
    def money_order_done(self):
        '''对收支单的审核按钮'''
        for order in self:
            if order.type == 'pay' and not order.partner_id.s_category_id.account_id:
                raise except_orm(u'错误', u'请输入供应商类别上的科目')
            if order.type == 'get' and not order.partner_id.c_category_id.account_id:
                raise except_orm(u'错误', u'请输入客户类别上的科目')
            if order.advance_payment < 0:
                raise except_orm(u'错误', u'核销金额不能大于付款金额')

            order.to_reconcile = order.advance_payment
            order.reconciled = order.amount - order.advance_payment

            total = 0
            for line in order.line_ids:
                if order.type == 'pay':  # 付款账号余额减少, 退款账号余额增加
                    if line.bank_id.balance < line.amount:
                        raise except_orm(u'错误', u'账户余额不足')
                    line.bank_id.balance -= line.amount
                else:  # 收款账号余额增加, 退款账号余额减少
                    line.bank_id.balance += line.amount
                total += line.amount

            if order.type == 'pay':
                order.partner_id.payable -= total + self.discount_amount
            else:
                order.partner_id.receivable -= total + self.discount_amount

            # 更新源单的未核销金额、已核销金额
            for source in order.source_ids:
                if abs(source.to_reconcile) < source.this_reconcile:
                    raise except_orm(u'错误', u'本次核销金额不能大于未核销金额')

                source.to_reconcile = (source.to_reconcile -
                                       source.this_reconcile)
                source.name.to_reconcile = source.to_reconcile
                source.name.reconciled = (source.reconciled +
                                          source.this_reconcile)

            order.state = 'done'
        return True

    @api.multi
    def money_order_draft(self):
        for order in self:
            order.to_reconcile = 0
            order.reconciled = 0

            total = 0
            for line in order.line_ids:
                if order.type == 'pay':  # 付款账号余额减少
                    line.bank_id.balance += line.amount
                else:  # 收款账号余额增加
                    if line.bank_id.balance < line.amount:
                        raise except_orm(u'错误', u'账户余额不足')
                    line.bank_id.balance -= line.amount
                total += line.amount

            if order.type == 'pay':
                order.partner_id.payable += total + self.discount_amount

            else:
                order.partner_id.receivable += total + self.discount_amount

            for source in order.source_ids:
                source.name.to_reconcile = (source.to_reconcile +
                                            source.this_reconcile)
                source.name.reconciled = (source.reconciled -
                                          source.this_reconcile)

            order.state = 'draft'
        return True
Example #18
0
class buy_summary_goods(models.Model):
    _name = 'buy.summary.goods'
    _inherit = 'report.base'
    _description = u'采购汇总表(按商品)'

    id_lists = fields.Text(u'移动明细行id列表')
    goods_categ = fields.Char(u'商品类别')
    goods_code = fields.Char(u'商品编码')
    goods = fields.Char(u'商品名称')
    attribute = fields.Char(u'属性')
    warehouse_dest = fields.Char(u'仓库')
    uos = fields.Char(u'辅助单位')
    qty_uos = fields.Float(u'辅助数量',
                           digits_compute=dp.get_precision('Quantity'))
    uom = fields.Char(u'基本单位')
    qty = fields.Float(u'基本数量', digits_compute=dp.get_precision('Quantity'))
    price = fields.Float(u'单价', digits_compute=dp.get_precision('Amount'))
    amount = fields.Float(u'采购金额', digits_compute=dp.get_precision('Amount'))
    tax_amount = fields.Float(u'税额', digits_compute=dp.get_precision('Amount'))
    subtotal = fields.Float(u'价税合计', digits_compute=dp.get_precision('Amount'))

    def select_sql(self, sql_type='out'):
        return '''
        SELECT MIN(wml.id) as id,
                array_agg(wml.id) AS id_lists,
                categ.name AS goods_categ,
                goods.code AS goods_code,
                goods.name AS goods,
                attr.name AS attribute,
                wh.name AS warehouse_dest,
                uos.name AS uos,
                SUM(wml.goods_uos_qty) AS qty_uos,
                uom.name AS uom,
                SUM(wml.goods_qty) AS qty,
                SUM(wml.amount) / SUM(wml.goods_qty) AS price,
                SUM(wml.amount) AS amount,
                SUM(wml.tax_amount) AS tax_amount,
                SUM(wml.subtotal) AS subtotal
        '''

    def from_sql(self, sql_type='out'):
        return '''
        FROM wh_move_line AS wml
            LEFT JOIN wh_move wm ON wml.move_id = wm.id
            LEFT JOIN partner ON wm.partner_id = partner.id
            LEFT JOIN goods ON wml.goods_id = goods.id
            LEFT JOIN core_category AS categ ON goods.category_id = categ.id
            LEFT JOIN attribute AS attr ON wml.attribute_id = attr.id
            LEFT JOIN warehouse AS wh ON wml.warehouse_dest_id = wh.id
            LEFT JOIN uom AS uos ON goods.uos_id = uos.id
            LEFT JOIN uom ON goods.uom_id = uom.id
        '''

    def where_sql(self, sql_type='out'):
        extra = ''
        if self.env.context.get('partner_id'):
            extra += 'AND partner.id = {partner_id}'
        if self.env.context.get('goods_id'):
            extra += 'AND goods.id = {goods_id}'
        if self.env.context.get('goods_categ_id'):
            extra += 'AND categ.id = {goods_categ_id}'

        return '''
        WHERE wml.state = 'done'
          AND wml.date >= '{date_start}'
          AND wml.date < '{date_end}'
          AND wm.origin like 'buy%%'
          %s
        ''' % extra

    def group_sql(self, sql_type='out'):
        return '''
        GROUP BY goods_categ,goods_code,goods,attribute,warehouse_dest,uos,uom
        '''

    def order_sql(self, sql_type='out'):
        return '''
        ORDER BY goods_code,goods,attribute,warehouse_dest
        '''

    def get_context(self, sql_type='out', context=None):
        date_end = datetime.datetime.strptime(
            context.get('date_end'), '%Y-%m-%d') + datetime.timedelta(days=1)
        date_end = date_end.strftime('%Y-%m-%d')
        return {
            'date_start':
            context.get('date_start') or '',
            'date_end':
            date_end,
            'partner_id':
            context.get('partner_id') and context.get('partner_id')[0] or '',
            'goods_id':
            context.get('goods_id') and context.get('goods_id')[0] or '',
            'goods_categ_id':
            context.get('goods_categ_id') and context.get('goods_categ_id')[0]
            or '',
        }

    def _compute_order(self, result, order):
        order = order or 'goods_code ASC'
        return super(buy_summary_goods, self)._compute_order(result, order)

    def collect_data_by_sql(self, sql_type='out'):
        collection = self.execute_sql(sql_type='out')
        return collection

    @api.multi
    def view_detail(self):
        '''采购汇总表(按商品)查看明细按钮'''
        line_ids = []
        res = []
        move_lines = []
        result = self.get_data_from_cache()
        for line in result:
            if line.get('id') == self.id:
                line_ids = line.get('id_lists')
                move_lines = self.env['wh.move.line'].search([('id', 'in',
                                                               line_ids)])

        for move_line in move_lines:
            details = self.env['buy.order.detail'].search([
                ('order_name', '=', move_line.move_id.name),
                ('goods_id', '=', move_line.goods_id.id)
            ])
            for detail in details:
                res.append(detail.id)

        return {
            'name': u'采购明细表',
            'view_mode': 'tree',
            'view_id': False,
            'res_model': 'buy.order.detail',
            'type': 'ir.actions.act_window',
            'domain': [('id', 'in', res)],
        }
Example #19
0
class PurchaseOrder(models.Model):
    _name = "purchase.order"
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _description = "Purchase Order"
    _order = 'date_order desc, id desc'

    @api.depends('order_line.price_total')
    def _amount_all(self):
        for order in self:
            amount_untaxed = amount_tax = 0.0
            for line in order.order_line:
                amount_untaxed += line.price_subtotal
                amount_tax += line.price_tax
            order.update({
                'amount_untaxed':
                order.currency_id.round(amount_untaxed),
                'amount_tax':
                order.currency_id.round(amount_tax),
                'amount_total':
                amount_untaxed + amount_tax,
            })

    @api.multi
    def _inverse_date_planned(self):
        for order in self:
            order.order_line.write({'date_planned': self.date_planned})

    @api.depends('order_line.date_planned')
    def _compute_date_planned(self):
        for order in self:
            min_date = False
            for line in order.order_line:
                if not min_date or line.date_planned < min_date:
                    min_date = line.date_planned
            if min_date:
                order.date_planned = min_date

    @api.depends('state', 'order_line.qty_invoiced', 'order_line.product_qty')
    def _get_invoiced(self):
        precision = self.env['decimal.precision'].precision_get(
            'Product Unit of Measure')
        for order in self:
            if order.state != 'purchase':
                order.invoice_status = 'no'
                continue

            if any(
                    float_compare(line.qty_invoiced,
                                  line.product_qty,
                                  precision_digits=precision) == -1
                    for line in order.order_line):
                order.invoice_status = 'to invoice'
            elif all(
                    float_compare(line.qty_invoiced,
                                  line.product_qty,
                                  precision_digits=precision) >= 0
                    for line in order.order_line):
                order.invoice_status = 'invoiced'
            else:
                order.invoice_status = 'no'

    @api.depends('order_line.invoice_lines.invoice_id.state')
    def _compute_invoice(self):
        for order in self:
            invoices = self.env['account.invoice']
            for line in order.order_line:
                invoices |= line.invoice_lines.mapped('invoice_id')
            order.invoice_ids = invoices
            order.invoice_count = len(invoices)

    @api.model
    def _default_picking_type(self):
        type_obj = self.env['stock.picking.type']
        company_id = self.env.context.get(
            'company_id') or self.env.user.company_id.id
        types = type_obj.search([('code', '=', 'incoming'),
                                 ('warehouse_id.company_id', '=', company_id)])
        if not types:
            types = type_obj.search([('code', '=', 'incoming'),
                                     ('warehouse_id', '=', False)])
        return types[0].id if types else False

    @api.depends('order_line.move_ids.picking_id')
    def _compute_picking(self):
        for order in self:
            pickings = self.env['stock.picking']
            for line in order.order_line:
                moves = line.move_ids.filtered(lambda r: r.state != 'cancel')
                pickings |= moves.mapped('picking_id')
            order.picking_ids = pickings
            order.picking_count = len(pickings)

    READONLY_STATES = {
        'purchase': [('readonly', True)],
        'done': [('readonly', True)],
        'cancel': [('readonly', True)],
    }

    name = fields.Char('Order Reference',
                       required=True,
                       select=True,
                       copy=False,
                       default='New')
    origin = fields.Char('Source Document', copy=False,\
        help="Reference of the document that generated this purchase order "
             "request (e.g. a sale order or an internal procurement request)")
    partner_ref = fields.Char('Vendor Reference', copy=False,\
        help="Reference of the sales order or bid sent by the vendor. "
             "It's used to do the matching when you receive the "
             "products as this reference is usually written on the "
             "delivery order sent by your vendor.")
    date_order = fields.Datetime('Order Date', required=True, states=READONLY_STATES, select=True, copy=False, default=fields.Datetime.now(),\
        help="Depicts the date where the Quotation should be validated and converted into a purchase order.")
    date_approve = fields.Date('Approval Date',
                               readonly=1,
                               select=True,
                               copy=False)
    partner_id = fields.Many2one('res.partner',
                                 string='Vendor',
                                 required=True,
                                 states=READONLY_STATES,
                                 change_default=True,
                                 track_visibility='always')
    dest_address_id = fields.Many2one('res.partner', string='Drop Ship Address', states=READONLY_STATES,\
        help="Put an address if you want to deliver directly from the vendor to the customer. "\
             "Otherwise, keep empty to deliver to your own company.")
    currency_id = fields.Many2one('res.currency', 'Currency', required=True, states=READONLY_STATES,\
        default=lambda self: self.env.user.company_id.currency_id.id)
    state = fields.Selection([('draft', 'Draft PO'), ('sent', 'RFQ Sent'),
                              ('to approve', 'To Approve'),
                              ('purchase', 'Purchase Order'), ('done', 'Done'),
                              ('cancel', 'Cancelled')],
                             string='Status',
                             readonly=True,
                             select=True,
                             copy=False,
                             default='draft',
                             track_visibility='onchange')
    order_line = fields.One2many('purchase.order.line',
                                 'order_id',
                                 string='Order Lines',
                                 states=READONLY_STATES,
                                 copy=True)
    notes = fields.Text('Terms and Conditions')

    invoice_count = fields.Integer(compute="_compute_invoice",
                                   string='# of Invoices',
                                   copy=False,
                                   default=0)
    invoice_ids = fields.Many2many('account.invoice',
                                   compute="_compute_invoice",
                                   string='Invoices',
                                   copy=False)
    invoice_status = fields.Selection([
        ('no', 'Not purchased'),
        ('to invoice', 'Waiting Invoices'),
        ('invoiced', 'Invoice Received'),
    ],
                                      string='Invoice Status',
                                      compute='_get_invoiced',
                                      store=True,
                                      readonly=True,
                                      copy=False,
                                      default='no')

    picking_count = fields.Integer(compute='_compute_picking',
                                   string='Receptions',
                                   default=0)
    picking_ids = fields.Many2many('stock.picking',
                                   compute='_compute_picking',
                                   string='Receptions',
                                   copy=False)

    date_planned = fields.Datetime(string='Scheduled Date',
                                   compute='_compute_date_planned',
                                   inverse='_inverse_date_planned',
                                   required=True,
                                   select=True,
                                   oldname='minimum_planned_date')

    amount_untaxed = fields.Monetary(string='Untaxed Amount',
                                     store=True,
                                     readonly=True,
                                     compute='_amount_all',
                                     track_visibility='always')
    amount_tax = fields.Monetary(string='Taxes',
                                 store=True,
                                 readonly=True,
                                 compute='_amount_all')
    amount_total = fields.Monetary(string='Total',
                                   store=True,
                                   readonly=True,
                                   compute='_amount_all')

    fiscal_position_id = fields.Many2one('account.fiscal.position',
                                         string='Fiscal Position',
                                         oldname='fiscal_position')
    payment_term_id = fields.Many2one('account.payment.term', 'Payment Term')
    incoterm_id = fields.Many2one(
        'stock.incoterms',
        'Incoterm',
        help=
        "International Commercial Terms are a series of predefined commercial terms used in international transactions."
    )

    product_id = fields.Many2one('product.product',
                                 related='order_line.product_id',
                                 string='Product')
    create_uid = fields.Many2one('res.users', 'Responsible')
    company_id = fields.Many2one(
        'res.company',
        'Company',
        required=True,
        select=1,
        states=READONLY_STATES,
        default=lambda self: self.env.user.company_id.id)

    picking_type_id = fields.Many2one('stock.picking.type', 'Deliver To', states=READONLY_STATES, required=True, default=_default_picking_type,\
        help="This will determine picking type of incoming shipment")
    group_id = fields.Many2one('procurement.group', string="Procurement Group")

    @api.model
    def name_search(self, name, args=None, operator='ilike', limit=100):
        args = args or []
        domain = []
        if name:
            domain = [
                '|', ('name', operator, name), ('partner_ref', operator, name)
            ]
        pos = self.search(domain + args, limit=limit)
        return pos.name_get()

    @api.multi
    @api.depends('name', 'partner_ref')
    def name_get(self):
        result = []
        for po in self:
            name = po.name
            if po.partner_ref:
                name += ' (' + po.partner_ref + ')'
            result.append((po.id, name))
        return result

    @api.model
    def create(self, vals):
        if vals.get('name', 'New') == 'New':
            vals['name'] = self.env['ir.sequence'].next_by_code(
                'purchase.order') or '/'
        return super(PurchaseOrder, self).create(vals)

    @api.multi
    def unlink(self):
        for order in self:
            if order.state not in ['draft', 'cancel']:
                raise UserError(
                    _('In order to delete a purchase order, you must cancel it first.'
                      ))
        return super(PurchaseOrder, self).unlink()

    @api.multi
    def _track_subtype(self, init_values):
        self.ensure_one()
        if 'state' in init_values and self.state == 'purchase':
            return 'purchase.mt_rfq_approved'
        elif 'state' in init_values and self.state == 'to approve':
            return 'purchase.mt_rfq_confirmed'
        elif 'state' in init_values and self.state == 'done':
            return 'purchase.mt_rfq_done'
        return super(PurchaseOrder, self)._track_subtype(init_values)

    @api.onchange('partner_id')
    def onchange_partner_id(self):
        if not self.partner_id:
            self.fiscal_position_id = False
            self.payment_term_id = False
            self.currency_id = False
        else:
            self.fiscal_position_id = self.env[
                'account.fiscal.position'].get_fiscal_position(
                    self.partner_id.id)
            self.payment_term_id = self.partner_id.property_supplier_payment_term_id.id
            self.currency_id = self.partner_id.property_purchase_currency_id.id or self.env.user.company_id.currency_id.id
        return {}

    @api.multi
    def action_rfq_send(self):
        '''
        This function opens a window to compose an email, with the edi purchase template message loaded by default
        '''
        self.ensure_one()
        ir_model_data = self.env['ir.model.data']
        try:
            if self.env.context.get('send_rfq', False):
                template_id = ir_model_data.get_object_reference(
                    'purchase', 'email_template_edi_purchase')[1]
            else:
                template_id = ir_model_data.get_object_reference(
                    'purchase', 'email_template_edi_purchase_done')[1]
        except ValueError:
            template_id = False
        try:
            compose_form_id = ir_model_data.get_object_reference(
                'mail', 'email_compose_message_wizard_form')[1]
        except ValueError:
            compose_form_id = False
        ctx = dict(self.env.context or {})
        ctx.update({
            'default_model': 'purchase.order',
            'default_res_id': self.ids[0],
            'default_use_template': bool(template_id),
            'default_template_id': template_id,
            'default_composition_mode': 'comment',
        })
        return {
            'name': _('Compose Email'),
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'mail.compose.message',
            'views': [(compose_form_id, 'form')],
            'view_id': compose_form_id,
            'target': 'new',
            'context': ctx,
        }

    @api.multi
    def print_quotation(self):
        self.write({'state': "sent"})
        return self.env['report'].get_action(
            self, 'purchase.report_purchasequotation')

    @api.multi
    def button_approve(self):
        self.write({'state': 'purchase'})
        self._create_picking()
        return {}

    @api.multi
    def button_draft(self):
        self.write({'state': 'draft'})
        return {}

    @api.multi
    def button_confirm(self):
        for order in self:
            order._add_supplier_to_product()
            # Deal with double validation process
            if order.company_id.po_double_validation == 'one_step'\
                    or (order.company_id.po_double_validation == 'two_step'\
                        and order.amount_total < self.env.user.company_id.currency_id.compute(order.company_id.po_double_validation_amount, order.currency_id))\
                    or order.user_has_groups('purchase.group_purchase_manager'):
                order.button_approve()
            else:
                order.write({'state': 'to approve'})
        return {}

    @api.multi
    def button_cancel(self):
        for order in self:
            for pick in order.picking_ids:
                if pick.state == 'done':
                    raise UserError(
                        _('Unable to cancel purchase order %s as some receptions have already been done.'
                          ) % (order.name))
            for inv in order.invoice_ids:
                if inv and inv.state not in ('cancel', 'draft'):
                    raise UserError(
                        _("Unable to cancel this purchase order.i You must first cancel related vendor bills."
                          ))

            for pick in order.picking_ids.filtered(
                    lambda r: r.state != 'cancel'):
                pick.action_cancel()
            if not self.env.context.get('cancel_procurement'):
                procurements = order.order_line.mapped('procurement_ids')
                procurements.filtered(lambda r: r.state not in (
                    'cancel', 'exception') and r.rule_id.propagate).write(
                        {'state': 'cancel'})
                procurements.filtered(lambda r: r.state not in (
                    'cancel', 'exception') and not r.rule_id.propagate).write(
                        {'state': 'exception'})
                moves = procurements.filtered(
                    lambda r: r.rule_id.propagate).mapped('move_dest_id')
                moves.filtered(lambda r: r.state != 'cancel').action_cancel()

        self.write({'state': 'cancel'})

    @api.multi
    def button_done(self):
        self.write({'state': 'done'})

    @api.multi
    def _get_destination_location(self):
        self.ensure_one()
        if self.dest_address_id:
            return self.dest_address_id.property_stock_customer.id
        return self.picking_type_id.default_location_dest_id.id

    @api.model
    def _prepare_picking(self):
        if not self.group_id:
            self.group_id = self.group_id.create({
                'name':
                self.name,
                'partner_id':
                self.partner_id.id
            })
        return {
            'picking_type_id': self.picking_type_id.id,
            'partner_id': self.partner_id.id,
            'date': self.date_order,
            'origin': self.name,
            'location_dest_id': self._get_destination_location(),
            'location_id': self.partner_id.property_stock_supplier.id
        }

    @api.multi
    def _create_picking(self):
        for order in self:
            ptypes = order.order_line.mapped('product_id.type')
            if ('product' in ptypes) or ('consu' in ptypes):
                res = order._prepare_picking()
                picking = self.env['stock.picking'].create(res)
                moves = order.order_line._create_stock_moves(picking)
                moves.action_confirm()
                moves.force_assign()
        return True

    @api.multi
    def _add_supplier_to_product(self):
        # Add the partner in the supplier list of the product if the supplier is not registered for
        # this product. We limit to 10 the number of suppliers for a product to avoid the mess that
        # could be caused for some generic products ("Miscellaneous").
        for line in self.order_line:
            if self.partner_id not in line.product_id.seller_ids.mapped(
                    'name') and len(line.product_id.seller_ids) <= 10:
                supplierinfo = {
                    'name':
                    self.partner_id.id,
                    'sequence':
                    max(line.product_id.seller_ids.mapped('sequence')) +
                    1 if line.product_id.seller_ids else 1,
                    'product_uom':
                    line.product_uom.id,
                    'min_qty':
                    0.0,
                    'price':
                    line.price_unit,
                    'currency_id':
                    self.partner_id.currency_id.id,
                    'delay':
                    0,
                }
                vals = {
                    'seller_ids': [(0, 0, supplierinfo)],
                }
                try:
                    line.product_id.write(vals)
                except AccessError:  # no write access rights -> just ignore
                    break

    @api.multi
    def action_view_picking(self):
        '''
        This function returns an action that display existing picking orders of given purchase order ids.
        When only one found, show the picking immediately.
        '''
        action = self.env.ref('stock.action_picking_tree')
        result = action.read()[0]

        #override the context to get rid of the default filtering on picking type
        result['context'] = {}
        pick_ids = sum([order.picking_ids.ids for order in self], [])
        #choose the view_mode accordingly
        if len(pick_ids) > 1:
            result['domain'] = "[('id','in',[" + ','.join(map(
                str, pick_ids)) + "])]"
        elif len(pick_ids) == 1:
            res = self.env.ref('stock.view_picking_form', False)
            result['views'] = [(res and res.id or False, 'form')]
            result['res_id'] = pick_ids and pick_ids[0] or False
        return result

    @api.multi
    def action_view_invoice(self):
        '''
        This function returns an action that display existing vendor bills of given purchase order ids.
        When only one found, show the vendor bill immediately.
        '''
        action = self.env.ref('account.action_invoice_tree2')
        result = action.read()[0]

        #override the context to get rid of the default filtering
        result['context'] = {
            'type': 'in_invoice',
            'default_purchase_id': self.id
        }
        result['domain'] = "[('purchase_id', '=', %s)]" % self.id
        invoice_ids = sum([order.invoice_ids.ids for order in self], [])
        #choose the view_mode accordingly
        if len(invoice_ids) > 1:
            result['domain'] = "[('id','in',[" + ','.join(map(
                str, invoice_ids)) + "])]"
        elif len(invoice_ids) == 1:
            res = self.env.ref('account.invoice_supplier_form', False)
            result['views'] = [(res and res.id or False, 'form')]
            result['res_id'] = invoice_ids and invoice_ids[0] or False
        return result
Example #20
0
class AccountantReport(models.Model):
    _name = "accountant.report"
    _description = "Accountant Report"
    _inherit = "mail.thread"

    @api.model
    def _default_name(self):
        return "/"

    @api.model
    def _default_company_id(self):
        return self.env.user.company_id.id

    @api.multi
    @api.depends(
        "state",
        "company_id",
        "service_id",
    )
    def _compute_policy(self):
        for report in self:
            if self.env.user.id == SUPERUSER_ID:
                report.confirm_ok = report.valid_ok = report.cancel_ok = \
                    report.restart_ok = True
                continue

            if not report.service_id:
                report.confirm_ok = report.valid_ok = \
                    report.cancel_ok = report.restart_ok = False
                continue

            report.confirm_ok = report._get_button_policy(
                "accountant_report_confirm_grp_ids")
            report.valid_ok = report._get_button_policy(
                "accountant_report_valid_grp_ids")
            report.cancel_ok = report._get_button_policy(
                "accountant_report_cancel_grp_ids")
            report.restart_ok = report._get_button_policy(
                "accountant_report_restart_grp_ids")

    @api.multi
    @api.depends(
        "service_id", )
    def _compute_signing_partner(self):
        obj_signing = self.env["accountant.report_signing_partner"]
        for report in self:
            result = []
            criteria = [
                ("service_id", "=", report.service_id.id),
            ]
            for signing in obj_signing.search(criteria):
                result.append(signing.signing_accountant_id.id)
            report.allowed_signing_accountant_ids = [(6, 0, result)]

    @api.multi
    @api.depends(
        "service_id", )
    def _compute_opinion(self):
        for report in self:
            report.allowed_opinion_ids = [
                (6, 0, report.service_id.allowed_opinion_ids.ids)
            ]

    name = fields.Char(
        string=_("# Report"),
        required=True,
        translate=False,
        default=lambda self: self._default_name(),
        readonly=True,
        copy=False,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    company_id = fields.Many2one(
        string=_("Company"),
        comodel_name="res.company",
        required=True,
        translate=False,
        default=lambda self: self._default_company_id(),
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    signing_accountant_id = fields.Many2one(
        string=_("Signing Accountant"),
        required=True,
        translate=False,
        comodel_name="res.partner",
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    partner_id = fields.Many2one(
        string=_("Customer"),
        required=True,
        translate=False,
        readonly=True,
        comodel_name="res.partner",
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    service_id = fields.Many2one(
        string=_("Accountant Service"),
        required=True,
        translate=False,
        readonly=True,
        comodel_name="accountant.service",
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    opinion_required = fields.Boolean(
        string="Opinion Required",
        related="service_id.opinion_required",
        readonly=True,
    )
    date = fields.Date(
        string=_("Date"),
        required=True,
        translate=False,
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    date_start = fields.Date(
        string=_("Date Start"),
        required=True,
        translate=False,
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    total_asset = fields.Float(string="Total Asset", )
    total_net_profit = fields.Float(string="Total Net Profit", )
    date_end = fields.Date(
        string=_("Date End"),
        required=True,
        translate=False,
        readonly=True,
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    assurance = fields.Boolean(string="Assurance", )
    report_opinion_id = fields.Many2one(
        string=_("Report Opinion"),
        required=False,
        translate=False,
        readonly=True,
        comodel_name="accountant.report_opinion",
        states={
            "draft": [
                ("readonly", False),
            ],
        },
    )
    note = fields.Text(string=_("Note"), )
    allowed_signing_accountant_ids = fields.Many2many(
        string="Allowed Signing Partner",
        comodel_name="res.partner",
        compute="_compute_signing_partner",
        store=False,
    )
    allowed_opinion_ids = fields.Many2many(
        string="Allowed Opinion",
        comodel_name="accountant.report_opinion",
        compute="_compute_opinion",
        store=False,
    )
    state = fields.Selection(
        string=_("State"),
        required=True,
        translate=False,
        readonly=True,
        selection=[
            ("draft", "Draft"),
            ("confirm", "Waiting for Approval"),
            ("valid", "Valid"),
            ("cancel", "Cancel"),
        ],
        default="draft",
        copy=False,
    )
    confirm_ok = fields.Boolean(
        string="Can Confirm",
        compute="_compute_policy",
        store=False,
        readonly=True,
    )
    valid_ok = fields.Boolean(
        string="Can Validate",
        compute="_compute_policy",
        store=False,
        readonly=True,
    )
    cancel_ok = fields.Boolean(
        string="Can Cancel",
        compute="_compute_policy",
        store=False,
        readonly=True,
    )
    restart_ok = fields.Boolean(
        string="Can Restart",
        compute="_compute_policy",
        store=False,
        readonly=True,
    )

    @api.multi
    def action_confirm(self):
        for report in self:
            report.write(self._prepare_confirm_data())

    @api.multi
    def action_valid(self):
        for report in self:
            report.write(self._prepare_valid_data())

    @api.multi
    def action_cancel(self):
        for report in self:
            report.write(self._prepare_cancel_data())

    @api.multi
    def action_restart(self):
        for report in self:
            report.write(self._prepare_restart_data())

    @api.multi
    def _prepare_confirm_data(self):
        self.ensure_one()
        result = {
            "state": "confirm",
        }
        return result

    @api.multi
    def _prepare_valid_data(self):
        self.ensure_one()
        result = {
            "state": "valid",
        }
        return result

    @api.multi
    def _prepare_cancel_data(self):
        self.ensure_one()
        result = {
            "state": "cancel",
        }
        return result

    @api.multi
    def _prepare_restart_data(self):
        self.ensure_one()
        result = {
            "state": "draft",
        }
        return result

    @api.model
    def _prepare_create_data(self, values):
        name = values.get("name", False)
        if not name or name == "/":
            values["name"] = self._create_sequence(
                values["service_id"], values["signing_accountant_id"])
        return values

    @api.model
    def _get_sequence(self, service_id, signing_accountant_id):
        obj_service = self.env["accountant.service"]
        company = self.env.user.company_id
        sequence_kategori = obj_service._get_sequence(service_id,
                                                      signing_accountant_id)

        if sequence_kategori:
            result = sequence_kategori
        elif company.accountant_report_sequence_id:
            result = company.accountant_report_sequence_id
        else:
            result = self.env.ref(
                "accountant_report.sequence_accountant_report")
        return result

    @api.model
    def _create_sequence(self, service_id, signing_accountant_id):
        name = self.env["ir.sequence"].\
            next_by_id(self._get_sequence(
                service_id, signing_accountant_id).id) or "/"
        return name

    @api.model
    def create(self, values):
        new_values = self._prepare_create_data(values)
        return super(AccountantReport, self).create(new_values)

    @api.multi
    def _get_button_policy(self, policy_field):
        self.ensure_one()
        result = False
        button_group_ids = []
        user = self.env.user
        group_ids = user.groups_id.ids

        button_group_ids = self.service_id._get_button_policy(
            policy_field, self.signing_accountant_id)

        button_group_ids += getattr(self.company_id, policy_field).ids

        if not button_group_ids:
            result = True
        else:
            if (set(button_group_ids) & set(group_ids)):
                result = True
        return result

    @api.onchange("service_id")
    def onchange_signing_accountant_id(self):
        self.signing_accountant_id = False

    @api.onchange("service_id")
    def onchange_report_opinion_id(self):
        self.report_opinion_id = False

    @api.multi
    def unlink(self):
        _super = super(AccountantReport, self)
        force_unlink = self._context.get("force_unlink", False)
        for report in self:
            if report.state != "draft" and not force_unlink:
                raise UserError(_("You can only delete data with draft state"))
        _super.unlink()
Example #21
0
class HrExpenseExpense(models.Model):

    _name = "hr.expense.expense"
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _description = "Expense"
    _order = "date desc"

    name = fields.Char(string='Expense Description')
    date = fields.Date(readonly=True, states={'draft': [('readonly', False)]}, default=fields.Date.context_today, string="Date")
    employee_id = fields.Many2one('hr.employee', string="Employee", required=True, readonly=True, states={'draft': [('readonly', False)]}, default=lambda self: self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1))
    untaxed_amount = fields.Float(string='Subtotal', store=True, compute='_compute_amount', digits=dp.get_precision('Account'))
    total_amount = fields.Float(string='Total', store=True, compute='_compute_amount', digits=dp.get_precision('Account'))
    company_id = fields.Many2one('res.company', string='Company', readonly=True, states={'draft': [('readonly', False)]}, default=lambda self: self.env.user.company_id)
    currency_id = fields.Many2one('res.currency', string='Currency', readonly=True, states={'draft': [('readonly', False)]}, default=lambda self: self.env.user.company_id.currency_id)
    analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account', states={'post': [('readonly', True)], 'done': [('readonly', True)]}, oldname='analytic_account', domain=[('account_type', '=', 'normal')])
    department_id = fields.Many2one('hr.department', string='Department', states={'post': [('readonly', True)], 'done': [('readonly', True)]})
    description = fields.Text()
    payment_mode = fields.Selection([("own_account", "Employee (to reimburse)"), ("company_account", "Company")], default='own_account', states={'done': [('readonly', True)], 'post': [('readonly', True)]}, string="Payment By")
    journal_id = fields.Many2one('account.journal', string='Expense Journal', states={'done': [('readonly', True)], 'post': [('readonly', True)]}, default=lambda self: self.env['account.journal'].search([('type', '=', 'purchase')], limit=1), help="The journal used when the expense is done.")
    bank_journal_id = fields.Many2one('account.journal', string='Bank Journal', states={'done': [('readonly', True)], 'post': [('readonly', True)]}, default=lambda self: self.env['account.journal'].search([('type', 'in', ['case', 'bank'])], limit=1), help="The payment method used when the expense is paid by the company.")
    account_move_id = fields.Many2one('account.move', string='Journal Entry', copy=False, track_visibility="onchange")
    attachment_number = fields.Integer(compute='_compute_attachment_number', string='Number of Attachments')
    state = fields.Selection([('draft', 'To Submit'),
                              ('submit', 'Submitted'),
                              ('approve', 'Approved'),
                              ('post', 'Waiting Payment'),
                              ('done', 'Paid'),
                              ('cancel', 'Refused')
                              ], string='Status', index=True, readonly=True, track_visibility='onchange', copy=False, default='draft', required=True,
        help='When the expense request is created the status is \'To Submit\'.\n It is submitted by the employee and request is sent to manager, the status is \'Submitted\'.\
        \nIf the manager approve it, the status is \'Approved\'.\n If the accountant genrate the accounting entries for the expense request, the status is \'Waiting Payment\'.')
    line_ids = fields.One2many('hr.expense', 'expense_id', 'Expense Lines', copy=True)

    @api.depends('line_ids.quantity', 'line_ids.unit_amount', 'line_ids.tax_ids', 'currency_id', 'state')
    def _compute_amount(self):
        untaxed_amount = 0
        total_amount = 0
        for expense in self.line_ids:
            if expense.state not in ('cancel'):
                untaxed_amount += expense.unit_amount * expense.quantity
                taxes = expense.tax_ids.compute_all(expense.unit_amount, expense.currency_id, expense.quantity, expense.product_id, expense.employee_id.user_id.partner_id)
                total_amount += taxes.get('total_included')
        self.untaxed_amount = untaxed_amount
        self.total_amount = total_amount

    @api.multi
    def _compute_attachment_number(self):
        attachment_data = self.env['ir.attachment'].read_group([('res_model', '=', 'hr.expense.expense'), ('res_id', 'in', self.ids)], ['res_id'], ['res_id'])
        attachment = dict((data['res_id'], data['res_id_count']) for data in attachment_data)
        for expense in self:
            expense.attachment_number = attachment.get(expense.id, 0)

    @api.onchange('employee_id')
    def _onchange_employee_id(self):
        self.department_id = self.employee_id.department_id

    def _add_followers(self):
        user_ids = []
        employee = self.employee_id
        if employee.user_id:
            user_ids.append(employee.user_id.id)
        if employee.parent_id:
            user_ids.append(employee.parent_id.user_id.id)
        if employee.department_id and employee.department_id.manager_id and employee.parent_id != employee.department_id.manager_id:
            user_ids.append(employee.department_id.manager_id.user_id.id)
        self.message_subscribe_users(user_ids=user_ids)

    # @api.model
    # def create(self, vals):
    #     hr_expense = super(HrExpenseExpense, self).create(vals)
    #     if vals.get('employee_id'):
    #         hr_expense._add_followers()
    #     try:
    #         if 'employee_id' in vals and vals['employee_id']!=False and 'state' in vals and vals['state']=='submit':
    #             #send an email
    #             notification_template = self.env['ir.model.data'].sudo().get_object('opencloud_hr_expense', 'responsavel_expense_email')

    #             employee = self.env['hr.employee'].search([('id', '=', vals['employee_id'])]).parent_id.work_email

    #             if employee:
    #                 notification_template.email_to = employee

    #                 notification_template.email_from = self.employee_id.work_email

    #                 notification_template.send_mail(self.id, False)
    #     except:
    #         rah='asdaasd'
    #     return hr_expense

    # @api.multi
    # def write(self, vals):
    #     res = super(HrExpenseExpense, self).write(vals)
    #     if vals.get('employee_id'):
    #         self._add_followers()
    #     try:
    #         if 'state' in vals and vals['state']=='submit':
    #             #send an email
    #             notification_template = self.env['ir.model.data'].sudo().get_object('opencloud_hr_expense', 'responsavel_expense_email')
    #             employee = self.employee_id.parent_id.work_email
    #             if employee:
    #                 notification_template.email_to = employee

    #                 notification_template.email_from = self.employee_id.work_email

    #                 notification_template.send_mail(self.id, False)
    #     except:
    #         rah='asdaasd'

    #     return res

    @api.multi
    def unlink(self):
        if any(expense.state not in ['draft', 'cancel'] for expense in self) or any(expense.state not in ['draft'] for expense in self.line_ids):
            raise UserError(_('You can only delete draft or refused expenses!'))
        return super(HrExpenseExpense, self).unlink()

    @api.multi
    def submit_expenses(self):
        if any(expense.state != 'draft' for expense in self) or any(expense.state != 'draft' for expense in self.line_ids):
            raise UserError(_("You can only submit draft expenses!"))
        self.write({'state': 'submit'})
        for i in self.line_ids:
            i.write({'state': 'submit'})

    @api.multi
    def approve_expenses(self):
        self.write({'state': 'approve'})
        for i in self.line_ids:
            if i.state not in ['cancel']:
                i.write({'state': 'approve'})

    @api.multi
    def refuse_expenses(self, reason):
        self.write({'state': 'cancel'})
        for i in self.line_ids:
            if i.state not in ['approve']:
                i.write({'state': 'cancel'})
        if self.employee_id.user_id:
            body = (_("Your Expense %s has been refused.<br/><ul class=o_timeline_tracking_value_list><li>Reason<span> : </span><span class=o_timeline_tracking_value>%s</span></li></ul>") % (self.name, reason))
            self.message_post(body=body, partner_ids=[self.employee_id.user_id.partner_id.id])

    @api.multi
    def paid_expenses(self):
        self.write({'state': 'done'})
        for i in self.line_ids:
            if i.state not in ['cancel']:
                i.write({'state': 'done'})

    @api.multi
    def reset_expenses(self):
        self.write({'state': 'draft'})
        for i in self.line_ids:
            i.write({'state': 'draft'})

    # @api.multi
    # def _track_subtype(self, init_values):
    #     self.ensure_one()
    #     if 'state' in init_values and self.state == 'approve':
    #         return 'hr_expense.mt_expense_approved'
    #     elif 'state' in init_values and self.state == 'submit':
    #         return 'hr_expense.mt_expense_confirmed'
    #     elif 'state' in init_values and self.state == 'cancel':
    #         return 'hr_expense.mt_expense_refused'
    #     return super(HrExpense, self)._track_subtype(init_values)

    def _prepare_move_line(self, line):
        '''
        This function prepares move line of account.move related to an expense
        '''
        partner_id = self.employee_id.address_home_id.commercial_partner_id.id
        return {
            'date_maturity': line.get('date_maturity'),
            'partner_id': partner_id,
            'name': line['name'][:64],
            'debit': round(line['price'],2) > 0 and round(line['price'],2),
            'credit': round(line['price'],2) < 0 and round(-line['price'],2),
            'account_id': line['account_id'],
            'analytic_line_ids': line.get('analytic_line_ids'),
            'amount_currency': line['price'] > 0 and abs(line.get('amount_currency')) or -abs(line.get('amount_currency')),
            'currency_id': line.get('currency_id'),
            'tax_line_id': line.get('tax_line_id'),
            'ref': line.get('ref'),
            'quantity': line.get('quantity',1.00),
            'product_id': line.get('product_id'),
            'product_uom_id': line.get('uom_id'),
            'analytic_account_id': line.get('analytic_account_id'),
        }

    @api.multi
    def _compute_expense_totals(self, company_currency, account_move_lines, move_date):
        '''
        internal method used for computation of total amount of an expense in the company currency and
        in the expense currency, given the account_move_lines that will be created. It also do some small
        transformations at these account_move_lines (for multi-currency purposes)

        :param account_move_lines: list of dict
        :rtype: tuple of 3 elements (a, b ,c)
            a: total in company currency
            b: total in hr.expense currency
            c: account_move_lines potentially modified
        '''
        self.ensure_one()
        total = 0.0
        total_currency = 0.0
        for line in account_move_lines:
            line['currency_id'] = False
            line['amount_currency'] = False
            if self.currency_id != company_currency:
                line['currency_id'] = self.currency_id.id
                line['amount_currency'] = line['price']
                line['price'] = self.currency_id.with_context(date=move_date or fields.Date.context_today(self)).compute(line['price'], company_currency, round=False)
            total -= line['price']
            total_currency -= line['amount_currency'] or line['price']
        return total, total_currency, account_move_lines

    @api.multi
    def action_move_create(self):
        '''
        main function that is called when trying to create the accounting entries related to an expense
        '''
        if any(expense.state not in ('approve','post') for expense in self) or any(expenses.state not in ('approve','cancel','post') for expenses in self.line_ids):
            raise UserError(_("You can only generate accounting entry for approved expense(s)."))

        if any(expense.employee_id != self[0].employee_id for expense in self) or any(expense.employee_id != self[0].employee_id for expense in self.line_ids):
            raise UserError(_("Expenses must belong to the same Employee."))

        if any(not expense.journal_id for expense in self) or any(not expense.journal_id for expense in self.line_ids):
            raise UserError(_("Expenses must have an expense journal specified to generate accounting entries."))

        journal_dict = {}
        maxdate = False
        # for expense in self:
        for expense in self.line_ids:
            if expense.state!='cancel':
                if expense.date > maxdate:
                    maxdate = expense.date
                jrn = expense.bank_journal_id if expense.payment_mode == 'company_account' else expense.journal_id
                journal_dict.setdefault(jrn, [])
                journal_dict[jrn].append(expense)

        for journal, expense_list in journal_dict.items():
            try:
                #create the move that will contain the accounting entries
                move = self.env['account.move'].create({
                    'journal_id': journal.id,
                    'company_id': self.env.user.company_id.id,
                    'date': maxdate,
                })
                for expense in expense_list:
                    company_currency = expense.company_id.currency_id
                    diff_currency_p = expense.currency_id != company_currency
                    #one account.move.line per expense (+taxes..)
                    move_lines = expense._move_line_get()

                    #create one more move line, a counterline for the total on payable account
                    total, total_currency, move_lines = expense._compute_expense_totals(company_currency, move_lines, maxdate)
                    if expense.payment_mode == 'company_account':
                        if not expense.bank_journal_id.default_credit_account_id:
                            raise UserError(_("No credit account found for the %s journal, please configure one.") % (expense.bank_journal_id.name))
                        emp_account = expense.bank_journal_id.default_credit_account_id.id
                    else:
                        if not expense.employee_id.address_home_id:
                            if not expense.employee_id.user_id or not expense.employee_id.user_id.partner_id:
                                raise UserError(_("No Home Address found for the employee %s, please configure one.") % (expense.employee_id.name))
                            else:
                                emp_account = expense.employee_id.user_id.partner_id.property_account_payable_id.id
                        else:
                            emp_account = expense.employee_id.address_home_id.property_account_payable_id.id

                    move_lines.append({
                            'type': 'dest',
                            'name': expense.employee_id.name,
                            'price': round(total,2),
                            'account_id': emp_account,
                            'date_maturity': expense.date,
                            'amount_currency': diff_currency_p and total_currency or False,
                            'currency_id': diff_currency_p and expense.currency_id.id or False,
                            'ref': expense.employee_id.address_home_id.ref or False
                            })

                    #convert eml into an osv-valid format
                    lines = map(lambda x:(0, 0, expense._prepare_move_line(x)), move_lines)
                    move.write({'line_ids': lines})
                    expense.write({'account_move_id': move.id, 'state': 'post'})

                    if expense.payment_mode == 'company_account':
                        expense.paid_expenses()
                move.post()
                if self.payment_mode == 'company_account':
                    self.paid_expenses()
                    self.write({'account_move_id': move.id})
                else:
                    self.write({'account_move_id': move.id, 'state': 'post'})

            except:
                #create the move that will contain the accounting entries
                move_lines2 = []
                for expense in expense_list:
                    company_currency = expense.company_id.currency_id
                    diff_currency_p = expense.currency_id != company_currency
                    #one account.move.line per expense (+taxes..)
                    move_lines = expense._move_line_get()

                    #create one more move line, a counterline for the total on payable account
                    total, total_currency, move_lines = expense._compute_expense_totals(company_currency, move_lines, maxdate)
                    if expense.payment_mode == 'company_account':
                        if not expense.bank_journal_id.default_credit_account_id:
                            raise UserError(_("No credit account found for the %s journal, please configure one.") % (expense.bank_journal_id.name))
                        emp_account = expense.bank_journal_id.default_credit_account_id.id
                    else:
                        if not expense.employee_id.address_home_id:
                            if not expense.employee_id.user_id or not expense.employee_id.user_id.partner_id:
                                raise UserError(_("No Home Address found for the employee %s, please configure one.") % (expense.employee_id.name))
                            else:
                                emp_account = expense.employee_id.user_id.partner_id.property_account_payable_id.id
                        else:
                            emp_account = expense.employee_id.address_home_id.property_account_payable_id.id

                    move_lines.append({
                            'type': 'dest',
                            'name': expense.employee_id.name,
                            'price': round(total,2),
                            'account_id': emp_account,
                            'date_maturity': expense.date,
                            'amount_currency': diff_currency_p and total_currency or False,
                            'currency_id': diff_currency_p and expense.currency_id.id or False,
                            'ref': expense.employee_id.address_home_id.ref or False
                            })

                    for pos in map(lambda x:(0, 0, expense._prepare_move_line(x)), move_lines):
                        move_lines2.append(pos)

                    if expense.payment_mode == 'company_account':
                        expense.paid_expenses()
                #### create do move ####
                move = self.env['account.move'].create({
                    'journal_id': journal.id,
                    'company_id': self.env.user.company_id.id,
                    'date': maxdate,
                    'line_ids': move_lines2
                })
                move.post()
                if self.payment_mode == 'company_account':
                    self.paid_expenses()
                    self.write({'account_move_id': move.id})
                    for i in self.line_ids:
                        i.write({'account_move_id': move.id})
                else:
                    self.write({'account_move_id': move.id, 'state': 'post'})
                    for i in self.line_ids:
                        i.write({'account_move_id': move.id, 'state': 'post'})

        return True

    @api.multi
    def _move_line_get(self):
        account_move = []
        for expense in self:
            if expense.product_id:
                account = expense.product_id.product_tmpl_id._get_product_accounts()['expense']
                if not account:
                    raise UserError(_("No Expense account found for the product %s (or for it's category), please configure one.") % (expense.product_id.name))
            else:
                account = self.env['ir.property'].with_context(force_company=expense.company_id.id).get('property_account_expense_categ_id', 'product.category')
                if not account:
                    raise UserError(_('Please configure Default Expense account for Product expense: `property_account_expense_categ_id`.'))
            move_line = {
                    'type': 'src',
                    'name': expense.name.split('\n')[0][:64],
                    'price_unit': expense.unit_amount,
                    'quantity': expense.quantity,
                    'price': round(expense.total_amount,2),
                    'account_id': account.id,
                    'product_id': expense.product_id.id,
                    'uom_id': expense.product_uom_id.id,
                    'analytic_account_id': expense.analytic_account_id.id,
                }
            account_move.append(move_line)

            # Calculate tax lines and adjust base line
            taxes = expense.tax_ids.compute_all(expense.unit_amount, expense.currency_id, expense.quantity, expense.product_id)
            account_move[-1]['price'] = round(taxes['total_excluded'],2)
            account_move[-1]['tax_ids'] = expense.tax_ids.id
            for tax in taxes['taxes']:
                account_move.append({
                    'type': 'tax',
                    'name': tax['name'],
                    'price_unit': tax['amount'],
                    'quantity': 1,
                    'price': round(tax['amount'],2),
                    'account_id': tax['account_id'] or move_line['account_id'],
                    'tax_line_id': tax['id'],
                })
        return account_move

    @api.multi
    def action_get_attachment_view(self):
        self.ensure_one()
        res = self.env['ir.actions.act_window'].for_xml_id('base', 'action_attachment')
        res['domain'] = [('res_model', '=', 'hr.expense.expense'), ('res_id', 'in', self.ids)]
        res['context'] = {'default_res_model': 'hr.expense.expense', 'default_res_id': self.id}
        return res
Example #22
0
class CostControl(ResCommon, models.Model):
    _name = 'cost.control'
    _inherit = ['mail.thread']
    _description = 'Job Order'

    @api.model
    def _get_owner_level_selection(self):
        selection = [
            ('org', 'Org'),
            ('sector', 'Sector'),
            ('subsector', 'Subsector'),
            ('division', 'Division'),
            ('section', 'Section'),
        ]
        return selection

    description = fields.Text(
        string='Description',
        size=1000,
    )
    cost_control_type_id = fields.Many2one(
        'cost.control.type',
        string='Job Order Type',
        required=True,
        track_visibility='onchange',
    )
    public = fields.Boolean(
        string="NSTDA Wide",
        copy=False,
        default=True,
        track_visibility='onchange',
    )
    owner_level = fields.Selection(
        string="Owner Level",
        selection=_get_owner_level_selection,
        copy=False,
        track_visibility='onchange',
    )
    # Unit Base
    org_id = fields.Many2one(
        'res.org',
        string='Org',
        track_visibility='onchange',
    )
    sector_id = fields.Many2one(
        'res.sector',
        string='Sector',
        track_visibility='onchange',
    )
    subsector_id = fields.Many2one(
        'res.subsector',
        string='Subsector',
        track_visibility='onchange',
    )
    division_id = fields.Many2one(
        'res.division',
        string='Division',
        track_visibility='onchange',
    )
    section_id = fields.Many2one(
        'res.section',
        string='Section',
        track_visibility='onchange',
    )
    active = fields.Boolean(
        track_visibility='onchange',
    )
    owner_name = fields.Char(
        string='Owner',
        compute='_check_owner',
    )

    _sql_constraints = [
        ('name_uniq', 'unique(name)', 'Job Order Name must be unique!'),
    ]

    @api.model
    def _check_access(self):
        if not self.env.user.has_group(
                'pabi_base.group_cooperate_budget')\
            and not self.env.user.has_group(
                'pabi_base.group_operating_unit_budget'):
            raise ValidationError(
                _('Sorry! \n You are not authorized to edit this field.'))
        return True

    @api.model
    def create(self, vals):
        if 'public' in vals:
            self._check_access()
        return super(CostControl, self).create(vals)

    @api.multi
    def write(self, vals):
        if 'public' in vals:
            self._check_access()
        return super(CostControl, self).write(vals)

    @api.onchange('public')
    def _onchange_public(self):
        self.owner_level = False
        self.org_id = False
        self.sector_id = False
        self.subsector_id = False
        self.division_id = False
        self.section_id = False

    @api.onchange('owner_level')
    def _onchange_owner_level(self):
        self.org_id = False
        self.sector_id = False
        self.subsector_id = False
        self.division_id = False
        self.section_id = False

    @api.depends('owner_level','org_id','sector_id','subsector_id','division_id','section_id')
    def _check_owner(self):
        for rec in self:
            if rec.owner_level == 'org':
                owner = rec.org_id.display_name
                rec.owner_name = owner
            if rec.owner_level == 'sector':
                owner = rec.sector_id.display_name
                rec.owner_name = owner
            if rec.owner_level == 'subsector':
                owner = rec.subsector_id.display_name
                rec.owner_name = owner
            if rec.owner_level == 'division':
                owner = rec.division_id.display_name
                rec.owner_name = owner
            if rec.owner_level == 'section':
                owner = rec.section_id.display_name
                rec.owner_name = owner
class Navintharh(models.Model):
    _inherit = 'hr.job'

    x_navintha_fechadeelaboracion = fields.Date(string="Fecha de elaboración")
    x_navintha_fechadeaprovacion = fields.Date(string="Fecha de aprobación")
    x_navintha_propositodelpuesto = fields.Text(string="Propósito del puesto")
    x_navintha_plantillaminimanecesaria = fields.Integer(
        string="Plantilla minima necesaria")

    x_navintha_relacionesinternas_delpuestoconotros = fields.Text(
        string="Relaciones internas con otros puestos")
    x_navintha_responsabilidades = fields.Many2one('res.partner',
                                                   string="Responsabilidades")
    x_navintha_relacionesexternas_delpuestoconotros = fields.Text(
        string="Relaciones externas con otros puestos")

    x_navintha_responsabilidades_descripcion = fields.Text(
        string="Responsabilidades y frecuencia de realización")
    x_navintha_responsabilidades_frecuenciaderealizacion = fields.Text(
        string="Frecuencia de realización")

    x_navintha_ltd_desicion = fields.Boolean(
        string="Libertad para toma de desiciones")

    x_navintha_condicionesdeltrabajo = fields.Text(
        string="Condiciones del trabajo")

    #Perfil del puesto
    x_navintha_edad = fields.Selection(selection=[('20 - 25', '20 - 25 Años'),
                                                  ('25 - 30', '25 - 30 Años'),
                                                  ('30 - 35', '30 - 35 Años'),
                                                  ('35 - 40', '35 - 40 Años'),
                                                  ('40 - 45', '40 - 45 Años')],
                                       string="Rango de edades")

    x_navintha_escolaridad = fields.Many2one('hr.escolaridad',
                                             string="Escolaridad")

    x_navintha_experiencia = fields.Text(string="Experiencia")
    x_navintha_competenciastecnicas = fields.Text(
        string="Competencias técnicas")
    x_navintha_manejomaquinariaequipo = fields.Text(
        string="Manejo de maquinaria y/o equipo")
    x_navintha_manejodesoftware = fields.Text(string="Manejo de software")
    x_navintha_idiomas = fields.Text(string="Idiomas")
    x_navintha_disponibilidadviajar = fields.Boolean(
        string="Disponibilidad para viajar")
    x_navintha_disponibilidadcambioresidencia = fields.Boolean(
        string="Disponibilidad de cambio de residencia")
    x_navintha_listadocompetencias = fields.Text(
        string="Listado de competencias")
    #Perfiles, competencias, habilidades y capacidades requeridas
    x_navintha_per_comp_hab_cap = fields.Text(
        string="Perfiles, competencias, habilidades y capacidades requeridas")

    x_navintha_skills = fields.Many2many('hr.skill', string="Habilidades")

    #x_navintha_equipoasignado = fields.Many2many('account.asset', string="Equipo asignable")
    x_navintha_indicadorespuesto = fields.Many2many(
        'kpi', string="Indicadores del puesto")
Example #24
0
class SaleOrder(models.Model):
    _inherit = 'sale.order'

    notify_approval = fields.Char(
        string=_(u'Notify approval'),
        size=100,
    )

    date_delivery = fields.Date(
        string=_(u'Date delivery'),
        default=fields.Date.today,
    )

    date_reception = fields.Date(
        string=_(u'Date reception'),
        default=fields.Date.today,
    )

    total_net_sale = fields.Float(
        string=_(u'Total net sale'),
        digits_compute=dp.get_precision('Account'),
        compute='_compute_profit_margin',
        store=True
    )

    perc_freight = fields.Float(
        string=_(u'Freight percentage'),
        digits_compute=dp.get_precision('Account'),
    )

    total_freight = fields.Float(
        string=_(u'Total Freight'),
        digits_compute=dp.get_precision('Account'),
        compute='_compute_profit_margin',
        store=True
    )

    perc_installation = fields.Float(
        string=_(u'installation percentage'),
        digits_compute=dp.get_precision('Account'),
    )

    total_installation = fields.Float(
        string=_(u'Total installation'),
        digits_compute=dp.get_precision('Account'),
        compute='_compute_profit_margin',
        store=True
    )

    profit_margin = fields.Float(
        string=_(u'Profit margin'),
        digits_compute=dp.get_precision('Account'),
        compute='_compute_profit_margin',
        store=True
    )

    not_be_billed = fields.Boolean(
        string=_(u'not be billed'),
    )

    manufacture = fields.Selection(
        [('special', _(u'Special')),
            ('line', _(u'Line')),
            ('replenishment', _(u'Replenishment')),
            ('semi_special', _(u'Semi special'))],
        string=_(u"Manufacture"),
    )

    executive = fields.Char(
        string=_(u'Executive'),
        size=100,
    )

    respo_reple = fields.Char(
        string=_(u'Responsible of replenishment'),
        size=200,
    )

    priority = fields.Selection(
        [('high', _(u'High')), ('replenishment', _(u'Replenishment')),
         ('express', _(u'Express')), ('sample', _(u'Sample'))],
        _(u'Manufacturing priority'),)

    complement_saleorder_id = fields.Many2one(
        'sale.order',
        string=_(u'In complement:'),
        help=_(u'Displays a list of sales orders'),
    )

    manufacturing_observations = fields.Text(
        string=_(u'Observations Manufacturing'),
    )

    replenishing_motif = fields.Text(
        string=_(u'Reason for the replenishment'),
    )

    credit_status = fields.Selection(
        [('normal', _(u'Normal')),
         ('suspended', _(u'Suspended for Collection')),
         ('conditioned', _(u'Conditioned'))],
        _(u'Credit status'),)

    credit_note = fields.Text(
        string=_(u'Note Credit and Collections'),
    )

    date_production = fields.Date(
        string=_('Date of Production Termination'),
    )

    approve = fields.Selection(
        [('approved', _('Approved')),
         ('suggested', _('Suggested for Approval')),
         ('not_approved', _('Not Approved'))],
        default='not_approved',
        string=_('Approve Status'),
        store=True,
        copy=False,
    )

    total_cost = fields.Float(
        string=_('Total cost'),
        compute='_compute_profit_margin',
        store=True
    )

    sale_picking_adm = fields.Boolean(
        string=_(u'Admin Sale Picking'),
    )

    webiste_operator = fields.Boolean(
        string=_('Captured by Operator'),
    )

    date_suggested = fields.Datetime(
        string=_('Suggestion Date Approval'),
        copy=False,
        help=_('Suggestion Date Approval.'))

    date_approved = fields.Datetime(
        string=_('Credit Release Date'),
        copy=False,
        help=_('Credit Release Date.'))

    _sql_constraints = [
        ('name_unique',
         'UNIQUE(name)',
         "The order name must be unique"),
    ]

    @api.onchange('partner_id')
    def _onchange_partner_id(self):
        self.webiste_operator = False
        self.notify_approval = False
        if self.partner_id:
            self.webiste_operator = True
        if self.partner_id.notify_approval:
            self.notify_approval = self.partner_id.notify_approval

    @api.depends('order_line.net_sale')
    def _compute_profit_margin(self):
        for order in self:
            global_cost = 0.0
            global_net_sale = 0.0
            global_freight = 0.0
            global_installa = 0.0
            global_profit_margin = 0.0
            currency = order.company_id.currency_id
            for line in order.order_line:
                global_cost += line.standard_cost
                global_net_sale += line.net_sale
                global_freight += line.freight_amount
                global_installa += line.installation_amount
            if global_net_sale > 0.000000:
                global_total_pm = currency.compute(
                    global_cost, order.pricelist_id.currency_id)
                global_profit_margin = (
                    1 - (global_total_pm) / global_net_sale)
                global_profit_margin = global_profit_margin * 100

            order.total_cost = global_cost
            order.total_net_sale = global_net_sale
            order.total_freight = global_freight
            order.total_installation = global_installa
            order.profit_margin = global_profit_margin

    @api.multi
    @api.onchange('project_id')
    def onchange_project_id(self):
        """
        Trigger the change of warehouse when the analytic account is modified.
        """
        if self.project_id and self.project_id.warehouse_id:
            self.warehouse_id = self.project_id.warehouse_id
        return {}

    @api.multi
    def action_confirm(self):
        for order in self:
            if order.company_id.is_manufacturer:
                order.validate_manufacturing()
                if not order.notify_approval:
                    raise UserError(
                        _('The following field is not invalid:\nNotify approval'))
                if not order.manufacture:
                    raise UserError(
                        _('The following field is not invalid:\nManufacture'))
                # if not order.executive:
                #     raise UserError(
                #         _('The following field is not invalid:\nExecutive'))
                if not order.priority:
                    raise UserError(
                        _('The following field is not invalid:\nManufacturing \
                          priority'))
                if not order.project_id:
                    raise UserError(
                        _('The following field is not invalid:\nAnalytic Account'))
                if not order.client_order_ref:
                    raise UserError(_('This Sale Order not has OC captured'))
                # Comented toda vez que ya hay un modulo de 
                # PLM que considera productos cotizacion:
                # for line in order.order_line:
                #     if line.product_id.quotation_product:
                #         raise UserError(_('The Product contains Quotation'))

        return super(SaleOrder, self).action_confirm()

    @api.multi
    def validate_manufacturing(self):
        for order in self:

            # pending = self.env['sale.order'].search(
            # [('state', '=', 'draft')])
            # dife = 0.0
            # dife = order.amount_total - order.total_nste
            # if order.total_nste > 0.0000000:
            #     if abs(dife) > 0.6000:
            #         raise UserError(
            #             _('The amount are differents:\nAnalytic Account'))

            for line in order.order_line:
                if line.product_id:
                    routes = line.product_id.route_ids + \
                        line.product_id.categ_id.total_route_ids
                    if len(routes) < 2:
                        raise UserError(
                            _('%s %s %s' % (
                                _("The next product has no a valid Route"),
                                line.product_id.default_code,
                                line.product_id.name)))
                    product_bom = False
                    for bom in line.product_id.product_tmpl_id.bom_ids:
                        if bom.product_id.id == line.product_id.id:
                            product_bom = bom or False
                    if not product_bom:
                        raise UserError(
                            _('%s %s %s' % (
                                _("The next product has no a Bill of Materials"),
                                line.product_id.default_code, line.product_id.name)))

                    # if not line.product_id.product_service_id:
                    #     raise UserError(
                    #         _('%s %s %s' % (
                    #             _("The next product has not a SAT Code: "),
                    #             line.product_id.default_code, line.product_id.name)))

        return True

    @api.multi
    def approve_action(self):
        for order in self:
            if order.approve == 'approved':
                raise UserError(_('This Sale Order is already approved'))
            if order.create_uid.id == order.write_uid.id:
                if order.manufacture != 'replenishment' or \
                   order.priority != 'replenishment':
                    raise UserError(_('Este no es un Pedido de Reposición solicita \
                    la aprobación de Credito y Cobranza.'))
            order.write({'approve': 'approved'})
            order.date_approved = fields.Datetime.now()

        # resws = super(SaleOrder, self)._product_data_validation()

        return True

    @api.multi
    def suggested_action(self):
        for order in self:
            if order.approve == 'suggested':
                raise UserError(_('This Sale Order is already Suggested for Approval'))
            if not order.order_line:
                raise UserError(_('This Sale Order not has Products Captured'))
            if not order.client_order_ref:
                raise UserError(_('This Sale Order not has OC captured'))
            if order.partner_id.parent_id:
                order.partner_id = order.partner_id.parent_id
            order.write({'approve': 'suggested'})
            order.date_suggested = fields.Datetime.now()

            if order.company_id.is_manufacturer:
                resws = order._product_data_validation()
        # if resws[0] != 'OK':
        #     raise ValidationError('Este pedido no podra ser aprobado  \
        #         debido a errores de configuracion \
        #         en los productos que ocasionarian \
        #         excepciones, se ha enviado un correo detallado a los \
        #         interesados.')

        return True

    @api.multi
    def _prepare_invoice(self):
        invoice_vals = super(SaleOrder, self)._prepare_invoice()
        invoice_vals['perc_freight'] = self.perc_freight
        invoice_vals['perc_installation'] = self.perc_installation
        # invoice_vals['executive'] = self.executive
        invoice_vals['manufacture'] = self.manufacture

        return invoice_vals

    @api.multi
    def action_done(self):
        super(SaleOrder, self).action_done()

        # commented temporary til implementatio of CRM
        self.force_quotation_send()

    @api.multi
    def force_quotation_send(self):
        for order in self:
            email_act = order.action_quotation_send()
            if email_act and email_act.get('context'):
                email_ctx = email_act['context']
                notify = order.notify_approval
                email_ctx.update(default_email_to=notify)
                email_ctx.update(default_email_from=order.company_id.email)
                order.with_context(email_ctx).message_post_with_template(email_ctx.get('default_template_id'))
        return True
class StockWarehouseTransferLine(models.Model):
    _name = 'stock.warehouse.transfer.line'
    _rec_name = 'product_id'

    @api.model
    def _get_default_product_qty(self):
        return 1.0

    product_id = fields.Many2one(comodel_name='product.product',
                                 string='Product')
    product_qty = fields.Float(string='Quantity',
                               default=_get_default_product_qty)
    product_uom_id = fields.Many2one(comodel_name='product.uom',
                                     string='Unit of Measure')
    transfer = fields.Many2one(comodel_name='stock.warehouse.transfer',
                               string='Transfer')
    note = fields.Text(string='Note')
    source_location = fields.Many2one(comodel_name='stock.location',
                                      string='Source Location',
                                      compute='_get_transfer_locations',
                                      store=True)
    dest_location = fields.Many2one(comodel_name='stock.location',
                                    string='Destination Location',
                                    compute='_get_transfer_locations',
                                    store=True)

    @api.one
    @api.onchange('product_id')
    def product_id_change(self):
        self.product_uom_id = self.product_id.uom_id and self.product_id.uom_id.id or False

    @api.multi
    @api.depends('transfer.source_warehouse', 'transfer.dest_warehouse')
    def _get_transfer_locations(self):
        for rec in self:
            rec.source_location = rec.transfer.source_warehouse.lot_stock_id.id
            dest_location = False
            transit_locations = self.env['stock.location'].search([
                ('usage', '=', 'transit')
            ])
            for location in transit_locations:
                if location.get_warehouse(
                        location) == rec.transfer.dest_warehouse.id:
                    dest_location = location.id

            if not dest_location:
                rec.dest_location = rec.transfer.dest_warehouse.lot_stock_id.id
            else:
                rec.dest_location = dest_location

    @api.multi
    def get_move_vals(self, picking, group):
        """
        Get the correct move values
        :param picking:
        :param group:
        :return: dict
        """

        self.ensure_one()
        return {
            'name': 'Warehouse Transfer',
            'product_id': self.product_id.id,
            'product_uom': self.product_uom_id.id,
            'product_uom_qty': self.product_qty,
            'location_id': self.source_location.id,
            'location_dest_id': self.dest_location.id,
            'picking_id': picking.id,
            'group_id': group.id,
            'note': self.note
        }
class Consult_Sale_Wizard(models.TransientModel):
    _name = 'consult.sale.wizard'

    consult_id = fields.Many2one(
        string='Consulta',
        comodel_name='clinic.consult',
        required=True,
    )
    product_tmpl_id = fields.Many2one(
        string='Servicio',
        comodel_name='product.template',
        required=True,
        related='consult_id.consulttype_id.product_tmpl_id')
    pricelist_id = fields.Many2one(
        string='Tarifa',
        comodel_name='product.pricelist',
        required=True,
    )
    # currency_id = fields.Many2one(
    #     string='Moneda',
    #     comodel_name='res.currency',
    # )
    date_order = fields.Datetime(string='Fecha', )
    company_id = fields.Many2one(
        string='Compañia',
        comodel_name='res.company',
    )

    note = fields.Text()

    @api.one
    def make_invoice_workflow(self, vals_order={}):
        obj_order = self.env['sale.order']
        invoice_obj = self.env['account.invoice']

        vals_order['user_id'] = self._uid
        new_id = obj_order.create(vals_order)

        #automatiza el flujo normal hasta la factura
        new_id.signal_workflow("order_confirm")
        new_id.signal_workflow("manual_invoice")
        inv_id = invoice_obj.search([('origin', '=', new_id.name)], limit=1)
        #inv_id.signal_workflow("invoice_open")
        return inv_id

    @api.multi
    def make_invoice(self, ):
        partner = self.consult_id.patient_id.partner_id
        fpos = partner.property_account_position and partner.property_account_position.id or False
        payment_term = partner.property_payment_term and partner.property_payment_term.id or False
        #partner_addr = partner_obj.address_get(self._cr, self._uid, [partner.id],
        #                                      ['default', 'invoice', 'delivery', 'contact'])

        if self._context.get('create_consult', False):
            order_line = []
            order_line.append((0, 0, {
                'product_id':
                self.consult_id.consulttype_id.product_id.id,
                'name':
                self.consult_id.consulttype_id.name,
                'quantity':
                1,
                'price_unit':
                self.consult_id.consulttype_id.product_tmpl_id.list_price
            }))

        # if self._context.get('invoice_examlab', False):
        #     order_line = []
        #     for t in self.consult_id.testlab_ids:
        #         order_line.append((0,
        #                            0,
        #                            {
        #                                'product_id': t.testtype_id.product_id.id,
        #                                'name':  t.testtype_id.product_id.name,
        #                                'quantity': 1,
        #                                'price_unit':  t.testtype_id.product_id.product_tmpl_id.list_price
        #                            }))
        # if self._context.get('invoice_examimage', False):
        #     order_line = []
        #     for t in self.consult_id.testimage_ids:
        #         order_line.append((0,
        #                            0,
        #                            {
        #                                'product_id': t.testtype_id.product_id.id,
        #                                'name': t.testtype_id.product_id.name,
        #                                'quantity': 1,
        #                                'price_unit':  t.testtype_id.product_id.product_tmpl_id.list_price
        #                            }))
        # if self._context.get('invoice_procedure', False):
        #     order_line = []
        #     for t in self.consult_id.procedure_ids:
        #         order_line.append((0,
        #                            0,
        #                            {
        #                                'product_id': t.procedure_id.product_id.id,
        #                                'name': t.procedure_id.product_id.name,
        #                                'quantity': 1,
        #                                'price_unit':  t.procedure_id.product_id.product_tmpl_id.list_price
        #                            }))
        # if self._context.get('invoice_prescription', False):
        #     order_line = []
        #     for t in self.consult_id.prescription_line_ids:
        #         order_line.append((0,
        #                            0,
        #                            {
        #                                'product_id': t.medicament_id.product_id.id,
        #                                'name': t.medicament_id.product_id.name,
        #                                'quantity': 1,
        #                                'price_unit':  t.medicament_id.product_id.product_tmpl_id.list_price
        #                            }))
        vals_consult = {
            'origin': self.consult_id.name,
            'partner_id': partner.id,
            'pricelist_id': self.pricelist_id.id,
            # 'partner_invoice_id': partner_addr['invoice'],
            'partner_invoice_id': partner.id,
            # 'partner_shipping_id': partner_addr['delivery'],
            'partner_shipping_id': partner.id,
            'date_order': fields.datetime.now(),
            'fiscal_position': fpos,
            'payment_term': payment_term,
            'order_line': order_line,
        }

        inv_id = self.make_invoice_workflow(vals_consult)

        if self._context.get('create_consult', False):
            self.consult_id.write({'state': 'pre_consult'})
        if self._context.get('create_consult', False):
            ir_model_data = self.env['ir.model.data']
            form_res = ir_model_data.get_object_reference(
                'account', 'invoice_form')
            form_id = form_res and form_res[1] or False

            res = {
                'view_mode': 'form',
                'nodestroy': True,
                'name': u'Comprobante de cliente',
                'context': "{'type':'out_invoice'}",
                'view_type': 'form',
                'res_model': 'account.invoice',
                'view_id': [(form_id, )],
                'type': 'ir.actions.act_window',
                'res_id': inv_id[0].id,
                'target': 'current'
            }

            return res
        return {'type': 'ir.actions.act_window_close'}
Example #27
0
class Planner(models.Model):
    """Planner Model.
    Each Planner has link to an ir.ui.view record that is a template used
    to display the planner pages.
    Each Planner has link to ir.ui.menu record that is a top menu used to display the
    planner launcher(progressbar)

    Method _prepare_<planner_application>_data(self, cr, uid, context) that
    generate the values used to display in specific planner pages
    """

    _name = 'web.planner'
    _description = 'Planner'

    @api.model
    def _get_planner_application(self):
        return []

    name = fields.Char(string='Name', required=True)
    menu_id = fields.Many2one('ir.ui.menu', string='Menu', required=True)
    view_id = fields.Many2one('ir.ui.view', string='Template', required=True)
    progress = fields.Integer(string="Progress Percentage", default=5)
    # data field is used to store the data filled by user in planner(JSON Data)
    data = fields.Text(string='Data')
    tooltip_planner = fields.Html(string='Planner Tooltips', translate=True)
    planner_application = fields.Selection('_get_planner_application',
                                           string='Planner Application',
                                           required=True)
    active = fields.Boolean(
        string="Active",
        default=True,
        help=
        "If the active field is set to False, it will allow you to hide the planner. This change requires a refresh of your page."
    )

    @api.model
    def render(self, template_id, planner_app):
        # prepare the planner data as per the planner application
        values = {
            'prepare_backend_url': self.prepare_backend_url,
            'is_module_installed': self.is_module_installed,
        }
        planner_find_method_name = '_prepare_%s_data' % planner_app
        if hasattr(self, planner_find_method_name):
            values.update(getattr(
                self, planner_find_method_name)())  # update the default value
        return self.env['ir.ui.view'].browse(template_id).render(values=values)

    @api.model
    def prepare_backend_url(self,
                            action_xml_id,
                            view_type='list',
                            module_name=None):
        """ prepare the backend url to the given action, or to the given module view.
            :param action_xml_id : the xml id of the action to redirect to
            :param view_type : the view type to display when redirecting (form, kanban, list, ...)
            :param module_name : the name of the module to display (if action_xml_id is 'open_module_tree'), or
                                 to redirect to if the action is not found.
            :returns url : the url to the correct page
        """
        params = dict(view_type=view_type)
        # setting the action
        action = self.env.ref(action_xml_id, False)
        if action:
            params['action'] = action.id
        else:
            params['model'] = 'ir.module.module'
        # setting the module
        if module_name:
            installed = self.env['ir.module.module']._installed()
            if module_name in installed:
                params['id'] = installed[module_name]
        return "/web#%s" % (urlencode(params), )

    @api.model
    def is_module_installed(self, module_name=None):
        return module_name in self.env['ir.module.module']._installed()
class AccountInvoice(models.Model):
    _inherit = 'account.invoice'

    @api.one
    @api.depends(
        'move_id.line_id.reconcile_id.line_id',
        'move_id.line_id.reconcile_partial_id.line_partial_ids',
    )
    def _compute_receivables(self):
        lines = self.env['account.move.line']
        for line in self.move_id.line_id:
            if line.account_id.id == self.account_id.id and \
                line.account_id.type in ('receivable', 'payable') and \
                    self.journal_id.revenue_expense:
                lines |= line
        self.move_line_receivable_id = (lines).sorted()

    @api.model
    def _default_fiscal_document(self):
        company = self.env['res.company'].browse(self.env.user.company_id.id)
        return company.service_invoice_id

    @api.model
    def _default_fiscal_document_serie(self):
        company = self.env['res.company'].browse(self.env.user.company_id.id)
        return company.document_serie_service_id

    issuer = fields.Selection([('0', u'Emissão própria'), ('1', 'Terceiros')],
                              'Emitente',
                              default='0',
                              readonly=True,
                              states={'draft': [('readonly', False)]})
    internal_number = fields.Char(
        'Invoice Number',
        size=32,
        readonly=True,
        states={'draft': [('readonly', False)]},
        help="""Unique number of the invoice, computed
            automatically when the invoice is created.""")
    fiscal_type = fields.Selection(PRODUCT_FISCAL_TYPE,
                                   'Tipo Fiscal',
                                   required=True,
                                   default=PRODUCT_FISCAL_TYPE_DEFAULT)
    vendor_serie = fields.Char(
        'Série NF Entrada',
        size=12,
        readonly=True,
        states={'draft': [('readonly', False)]},
        help=u"Série do número da Nota Fiscal do Fornecedor")
    move_line_receivable_id = fields.Many2many('account.move.line',
                                               string='Receivables',
                                               compute='_compute_receivables')
    document_serie_id = fields.Many2one(
        'l10n_br_account.document.serie',
        string=u'Série',
        domain="[('fiscal_document_id', '=', fiscal_document_id),\
        ('company_id','=',company_id)]",
        readonly=True,
        states={'draft': [('readonly', False)]},
        default=_default_fiscal_document_serie)
    fiscal_document_id = fields.Many2one(
        'l10n_br_account.fiscal.document',
        string='Documento',
        readonly=True,
        states={'draft': [('readonly', False)]},
        default=_default_fiscal_document)
    fiscal_document_electronic = fields.Boolean(
        related='fiscal_document_id.electronic',
        type='boolean',
        readonly=True,
        store=True,
        string='Electronic')
    fiscal_category_id = fields.Many2one(
        'l10n_br_account.fiscal.category',
        'Categoria Fiscal',
        readonly=True,
        states={'draft': [('readonly', False)]})
    fiscal_position = fields.Many2one(
        'account.fiscal.position',
        'Fiscal Position',
        readonly=True,
        states={'draft': [('readonly', False)]},
        domain="[('fiscal_category_id','=',fiscal_category_id)]")
    account_document_event_ids = fields.One2many(
        'l10n_br_account.document_event', 'document_event_ids', u'Eventos')
    fiscal_comment = fields.Text(u'Observação Fiscal')

    _order = 'internal_number desc'

    @api.one
    @api.constrains('number')
    def _check_invoice_number(self):
        domain = []
        if self.number:
            fiscal_document = self.fiscal_document_id and\
                self.fiscal_document_id.id or False
            domain.extend([('internal_number', '=', self.number),
                           ('fiscal_type', '=', self.fiscal_type),
                           ('fiscal_document_id', '=', fiscal_document)])
            if self.issuer == '0':
                domain.extend([('company_id', '=', self.company_id.id),
                               ('internal_number', '=', self.number),
                               ('fiscal_document_id', '=',
                                self.fiscal_document_id.id),
                               ('issuer', '=', '0')])
            else:
                domain.extend([('partner_id', '=', self.partner_id.id),
                               ('vendor_serie', '=', self.vendor_serie),
                               ('issuer', '=', '1')])

            invoices = self.env['account.invoice'].search(domain)
            if len(invoices) > 1:
                raise UserError(u'Não é possível registrar documentos\
                              fiscais com números repetidos.')

    _sql_constraints = [
        ('number_uniq', 'unique(number, company_id, journal_id,\
         type, partner_id)', 'Invoice Number must be unique per Company!'),
    ]

    # TODO não foi migrado por causa do bug github.com/odoo/odoo/issues/1711
    def fields_view_get(self,
                        cr,
                        uid,
                        view_id=None,
                        view_type=False,
                        context=None,
                        toolbar=False,
                        submenu=False):
        result = super(AccountInvoice,
                       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 not view_type:
            view_id = self.pool.get('ir.ui.view').search(
                cr, uid, [('name', '=', 'account.invoice.tree')])
            view_type = 'tree'

        if view_type == 'form':
            eview = etree.fromstring(result['arch'])

            if 'type' in context.keys():
                fiscal_types = eview.xpath("//field[@name='invoice_line']")
                for fiscal_type in fiscal_types:
                    fiscal_type.set(
                        'context', "{'type': '%s', 'fiscal_type': '%s'}" %
                        (context['type'], context.get('fiscal_type',
                                                      'service')))

                fiscal_categories = eview.xpath(
                    "//field[@name='fiscal_category_id']")
                for fiscal_category_id in fiscal_categories:
                    fiscal_category_id.set(
                        'domain',
                        """[('fiscal_type', '=', '%s'), ('type', '=', '%s'),
                        ('state', '=', 'approved'),
                        ('journal_type', '=', '%s')]""" %
                        (context.get('fiscal_type', 'service'),
                         OPERATION_TYPE[context['type']],
                         JOURNAL_TYPE[context['type']]))
                    fiscal_category_id.set('required', '1')

                document_series = eview.xpath(
                    "//field[@name='document_serie_id']")
                for document_serie_id in document_series:
                    document_serie_id.set(
                        'domain', "[('fiscal_type', '=', '%s')]" %
                        (context.get('fiscal_type', 'service')))

            if context.get('fiscal_type', False):
                delivery_infos = eview.xpath("//group[@name='delivery_info']")
                for delivery_info in delivery_infos:
                    delivery_info.set('invisible', '1')

            result['arch'] = etree.tostring(eview)

        if view_type == 'tree':
            doc = etree.XML(result['arch'])
            nodes = doc.xpath("//field[@name='partner_id']")
            partner_string = _('Customer')
            if context.get('type', 'out_invoice') in \
                    ('in_invoice', 'in_refund'):
                partner_string = _('Supplier')
            for node in nodes:
                node.set('string', partner_string)
            result['arch'] = etree.tostring(doc)
        return result

    @api.multi
    def action_number(self):
        # TODO: not correct fix but required a fresh values before reading it.
        self.write({})

        for invoice in self:
            if invoice.issuer == '0':
                sequence_obj = self.env['ir.sequence']
                sequence = sequence_obj.browse(
                    invoice.document_serie_id.internal_sequence_id.id)
                invalid_number = self.env[
                    'l10n_br_account.invoice.invalid.number'].search([
                        ('number_start', '<=', sequence.number_next),
                        ('number_end', '>=', sequence.number_next),
                        ('state', '=', 'done')
                    ])

                if invalid_number:
                    raise except_orm(
                        _(u'Número Inválido !'),
                        _("O número: %s da série: %s, esta inutilizado") %
                        (sequence.number_next, invoice.document_serie_id.name))

                seq_number = sequence_obj.get_id(
                    invoice.document_serie_id.internal_sequence_id.id)
                self.write({
                    'internal_number': seq_number,
                    'number': seq_number
                })
        return True

    # TODO Talvez este metodo substitui o metodo action_move_create
    @api.multi
    def finalize_invoice_move_lines(self, move_lines):
        """ finalize_invoice_move_lines(move_lines) -> move_lines

            Hook method to be overridden in additional modules to verify and
            possibly alter the move lines to be created by an invoice, for
            special cases.
            :param move_lines: list of dictionaries with the account.move.lines
            (as for create())
            :return: the (possibly updated) final move_lines to create for this
            invoice
        """
        move_lines = super(AccountInvoice,
                           self).finalize_invoice_move_lines(move_lines)
        count = 1
        result = []
        for move_line in move_lines:
            if move_line[2]['debit'] or move_line[2]['credit']:
                if move_line[2]['account_id'] == self.account_id.id:
                    move_line[2]['name'] = '%s/%s' % \
                        (self.internal_number, count)
                    count += 1
                result.append(move_line)
        return result

    def _fiscal_position_map(self, result, **kwargs):
        ctx = dict(self._context)
        ctx.update({'use_domain': ('use_invoice', '=', True)})
        if ctx.get('fiscal_category_id'):
            kwargs['fiscal_category_id'] = ctx.get('fiscal_category_id')

        if not kwargs.get('fiscal_category_id'):
            return result

        company = self.env['res.company'].browse(kwargs.get('company_id'))

        fcategory = self.env['l10n_br_account.fiscal.category'].browse(
            kwargs.get('fiscal_category_id'))
        result['value']['journal_id'] = fcategory.property_journal.id
        if not result['value'].get('journal_id', False):
            raise except_orm(
                _('Nenhum Diário !'),
                _("Categoria fiscal: '%s', não tem um diário contábil para a \
                empresa %s") % (fcategory.name, company.name))
        return self.env['account.fiscal.position.rule'].with_context(
            ctx).apply_fiscal_mapping(result, **kwargs)

    @api.multi
    def onchange_fiscal_category_id(self, partner_address_id, partner_id,
                                    company_id, fiscal_category_id):
        result = {'value': {'fiscal_position': None}}
        return self._fiscal_position_map(result,
                                         partner_id=partner_id,
                                         partner_invoice_id=partner_address_id,
                                         company_id=company_id,
                                         fiscal_category_id=fiscal_category_id)

    @api.onchange('fiscal_document_id')
    def onchange_fiscal_document_id(self):
        if self.issuer == '0':
            self.document_serie_id = self.company_id.document_serie_service_id
class ProductSupplierinfoTender(models.Model):
    _name = "product.supplierinfo.tender"
    _description = "Supplier Pricelist Tender"
    _inherit = ['mail.thread', 'ir.needaction_mixin']

    name = fields.Char(string='Reference',
                       required=True,
                       copy=False,
                       default='New',
                       readonly=True)
    state = fields.Selection([('draft', 'Draft'), ('open', 'Call for Bids'),
                              ('selection', 'Bid Selection'),
                              ('done', 'Completed'), ('cancel', 'Cancelled')],
                             string='Status',
                             select=True,
                             copy=False,
                             default='draft',
                             track_visibility='onchange')
    date_end = fields.Datetime(string='Tender Closing Deadline', copy=False)
    user_id = fields.Many2one(comodel_name='res.users', string='Responsible')
    currency_id = fields.Many2one(
        'res.currency',
        'Currency',
        required=True,
        states=PREPARATION_STATES,
        default=lambda self: self.env.user.company_id.currency_id.id)
    description = fields.Text(string='Description')
    company_id = fields.Many2one(
        'res.company',
        'Company',
        required=True,
        select=1,
        states=PREPARATION_STATES,
        default=lambda self: self.env.user.company_id.id)
    line_ids = fields.One2many(comodel_name='product.supplierinfo.tender.line',
                               inverse_name='tender_id',
                               string='Tender Lines',
                               states=SELECTION_STATES,
                               copy=True)
    bid_ids = fields.One2many(comodel_name='product.supplierinfo.bid',
                              inverse_name='tender_id',
                              string='Bids',
                              states=SELECTION_STATES,
                              readonly=True,
                              copy=False)
    product_supplierinfo_ids = fields.One2many(
        comodel_name='product.supplierinfo',
        inverse_name='tender_id',
        string='Supplier Pricelists',
        states=PREPARATION_STATES,
        copy=False)

    @api.model
    def create(self, vals):
        if vals.get('name', 'New') == 'New':
            vals['name'] = self.env['ir.sequence'].next_by_code(
                'product.supplierinfo.tender') or '/'
        return super(ProductSupplierinfoTender, self).create(vals)

    @api.multi
    def button_draft(self):
        for rec in self:
            rec.state = 'draft'
            rec.bid_ids.button_open()

    @api.multi
    def button_draft_manager(self):
        self.button_draft()

    @api.multi
    def button_open(self):
        for rec in self:
            rec.state = 'open'

    @api.multi
    def button_selection(self):
        for rec in self:
            rec.state = 'selection'
            rec.bid_ids.button_close()

    @api.multi
    def button_done(self):
        for rec in self:
            rec.state = 'done'

    @api.multi
    def button_cancel(self):
        for rec in self:
            rec.state = 'cancel'

    @api.model
    def _prepare_bid(self, tender, supplier):
        return {
            'partner_id': supplier.id,
            'currency_id': tender.currency_id.id,
            'tender_id': tender.id
        }

    @api.model
    def _prepare_supplierinfo(self, tender, line, bid_id, supplier):
        return {
            'bid_id': bid_id,
            'name': supplier.id,
            'product_tmpl_id': line.product_id.product_tmpl_id.id,
            'product_id': line.product_id.id,
            'company_id': tender.company_id.id,
            'currency_id': tender.currency_id.id,
            'min_qty': line.min_qty
        }

    @api.multi
    def make_bid(self, partner_id):
        assert partner_id, 'Vendor should be specified'
        bid_model = self.env['product.supplierinfo.bid']
        supplierinfo_model = self.env['product.supplierinfo']
        partner_model = self.env['res.partner']
        supplier = partner_model.browse(partner_id)
        res = {}
        for tender in self:
            bid = bid_model.create(self._prepare_bid(tender, supplier))
            bid.message_post(body=_("Bid created"))
            res[tender.id] = bid.id
            for line in tender.line_ids:
                supplierinfo_model.create(
                    self._prepare_supplierinfo(tender, line, bid.id, supplier))
        return res
Example #30
0
class account_voucher_line(models.Model):
    _name = 'account.voucher.line'
    _description = 'Voucher Lines'

    @api.one
    @api.depends('price_unit', 'tax_ids', 'quantity', 'product_id',
                 'voucher_id.currency_id')
    def _compute_subtotal(self):
        self.price_subtotal = self.quantity * self.price_unit
        if self.tax_ids:
            taxes = self.tax_ids.compute_all(
                self.price_unit,
                self.voucher_id.currency_id,
                self.quantity,
                product=self.product_id,
                partner=self.voucher_id.partner_id)
            self.price_subtotal = taxes['total_excluded']

    name = fields.Text(string='Description', required=True)
    sequence = fields.Integer(
        default=10,
        help="Gives the sequence of this line when displaying the voucher.")
    voucher_id = fields.Many2one('account.voucher',
                                 'Voucher',
                                 required=1,
                                 ondelete='cascade')
    product_id = fields.Many2one('product.product',
                                 string='Product',
                                 ondelete='set null',
                                 index=True)
    account_id = fields.Many2one(
        'account.account',
        string='Account',
        required=True,
        domain=[('deprecated', '=', False)],
        help="The income or expense account related to the selected product.")
    price_unit = fields.Float(string='Unit Price',
                              required=True,
                              digits=dp.get_precision('Product Price'),
                              oldname='amount')
    price_subtotal = fields.Monetary(string='Amount',
                                     store=True,
                                     readonly=True,
                                     compute='_compute_subtotal')
    quantity = fields.Float(digits=dp.get_precision('Product Unit of Measure'),
                            required=True,
                            default=1)
    account_analytic_id = fields.Many2one('account.analytic.account',
                                          'Analytic Account')
    company_id = fields.Many2one('res.company',
                                 related='voucher_id.company_id',
                                 string='Company',
                                 store=True,
                                 readonly=True)
    tax_ids = fields.Many2many('account.tax',
                               string='Tax',
                               help="Only for tax excluded from price")
    currency_id = fields.Many2one('res.currency',
                                  related='voucher_id.currency_id')

    def _get_account(self, product, fpos, type):
        accounts = product.product_tmpl_id.get_product_accounts(fpos)
        if type == 'sale':
            return accounts['income']
        return accounts['expense']

    @api.multi
    def product_id_change(self,
                          product_id,
                          partner_id=False,
                          price_unit=False,
                          company_id=None,
                          currency_id=None,
                          type=None):
        context = self._context
        company_id = company_id if company_id is not None else context.get(
            'company_id', False)
        company = self.env['res.company'].browse(company_id)
        currency = self.env['res.currency'].browse(currency_id)
        if not partner_id:
            raise UserError(_("You must first select a partner!"))
        part = self.env['res.partner'].browse(partner_id)
        if part.lang:
            self = self.with_context(lang=part.lang)

        product = self.env['product.product'].browse(product_id)
        fpos = part.property_account_position_id.id
        account = self._get_account(product, fpos, type)
        values = {
            'name': product.partner_ref,
            'account_id': account.id,
        }

        if type == 'purchase':
            values['price_unit'] = price_unit or product.standard_price
            taxes = product.supplier_taxes_id or account.tax_ids
            if product.description_purchase:
                values['name'] += '\n' + product.description_purchase
        else:
            values['price_unit'] = product.lst_price
            taxes = product.taxes_id or account.tax_ids
            if product.description_sale:
                values['name'] += '\n' + product.description_sale

        values['tax_ids'] = taxes.ids

        if company and currency:
            if company.currency_id != currency:
                if type == 'purchase':
                    values['price_unit'] = product.standard_price
                values['price_unit'] = values['price_unit'] * currency.rate

        return {'value': values, 'domain': {}}