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)
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
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')
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
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
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
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)
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(
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
class ubicacion(models.Model): _name = 'it_tools.ubicacion' name = fields.Char(string='NombreCorto', required=True) description = fields.Text()
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)
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
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)
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
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)], }
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
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()
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
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")
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'}
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
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': {}}