class AccountJournal(models.Model): _inherit = "account.journal" # redefine el type original para que sea igual que 8 y no haga falta cambiar # el tratamiento type = fields.Selection([ ('sale', 'Sale'), ('sale_refund', 'Sale Refunds'), ('purchase', 'Purchase'), ('purchase_refund', 'Purchase Refunds'), ('cash', 'Cash'), ('bank', 'Bank'), ('general', 'General'), ], string='Type') sucursal_id = fields.Many2one('sii.sucursal', string="Sucursal") sii_code = fields.Char(related='sucursal_id.name', string="Código SII Sucursal", readonly=True) journal_document_class_ids = fields.One2many( 'account.journal.sii_document_class', 'journal_id', 'Documents Class', ) point_of_sale_id = fields.Many2one('sii.point_of_sale', 'Point of sale') point_of_sale = fields.Integer(related='point_of_sale_id.number', string='Point of sale', readonly=True) use_documents = fields.Boolean('Use Documents?', default='_get_default_doc') document_sequence_type = fields.Selection( [('own_sequence', 'Own Sequence'), ('same_sequence', 'Same Invoice Sequence')], string='Document Sequence Type', help="Use own sequence or invoice sequence on Debit and Credit \ Notes?") journal_activities_ids = fields.Many2many( 'partner.activities', id1='journal_id', id2='activities_id', string='Journal Turns', help="""Select the turns you want to \ invoice in this Journal""") excempt_documents = fields.Boolean('Exempt Documents Available', compute='_check_activities') @api.multi def _get_default_doc(self): self.ensure_one() if 'sale' in self.type or 'purchase' in self.type: self.use_documents = True @api.one @api.depends('journal_activities_ids', 'type') def _check_activities(self): # self.ensure_one() # si entre los giros del diario hay alguno que está excento # el boolean es True try: if 'purchase' in self.type: self.excempt_documents = True elif 'sale' in self.type: no_vat = False for turn in self.journal_activities_ids: print('turn %s' % turn.vat_affected) if turn.vat_affected == 'SI': continue else: no_vat = True break self.excempt_documents = no_vat except: pass @api.one @api.constrains('point_of_sale_id', 'company_id') def _check_company_id(self): if self.point_of_sale_id and self.point_of_sale_id.company_id != self.company_id: raise Warning( _('The company of the point of sale and of the \ journal must be the same!'))
class HrDeputation(models.Model): _name = 'hr.deputation' _inherit = ['mail.thread'] _order = 'id desc' _rec_name = 'order_date' _description = u'الانتدابات' @api.model def fields_view_get(self, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): # Objects user_obj = self.env['res.users'] uid = self._uid user = user_obj.browse(uid) if self._context.get('params', False): active_id = self._context.get('params', False).get('id', False) deputation_obj = self.env['hr.deputation'].search([('id', '=', int(active_id))], limit=1) res = super(HrDeputation, self).fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu) if view_type == 'form' and deputation_obj: arch = etree.XML(res['arch']) is_exelence = user.has_group('smart_hr.group_exelence_employee') secret_report = deputation_obj.secret_report if secret_report: if is_exelence is False: for node in arch.xpath("//field[@name='lettre_number']"): node.set('invisible', '1') setup_modifiers(node, res['fields']['lettre_number']) for node in arch.xpath("//field[@name='lettre_date']"): node.set('invisible', '1') setup_modifiers(node, res['fields']['lettre_date']) for node in arch.xpath("//field[@name='file_lettre']"): node.set('invisible', '1') setup_modifiers(node, res['fields']['file_lettre']) # Get current user group res['arch'] = etree.tostring(arch, encoding="utf-8") return res order_date = fields.Date(string='تاريخ الطلب', default=fields.Datetime.now(), readonly=1) employee_id = fields.Many2one('hr.employee', string=' إسم الموظف', domain=[('employee_state', '=', 'employee'), ('emp_state', 'not in', ['suspended', 'terminated'])], required=1) number = fields.Char(string='الرقم الوظيفي', readonly=1) code = fields.Char(string=u'رمز الوظيفة ', readonly=1) governmental_entity = fields.Many2one('res.partner', string=u'الجهة ', domain=['|', ('company_type', '=', 'governmental_entity'), ('company_type', '=', 'company')]) country_id = fields.Many2one(related='employee_id.country_id', store=True, readonly=True, string='الجنسية') number_job = fields.Char(string='رقم الوظيفة', store=True, readonly=1) job_id = fields.Many2one('hr.job', string='الوظيفة', store=True, readonly=1) type_id = fields.Many2one('salary.grid.type', string='الصنف', store=True, readonly=1) department_id = fields.Many2one('hr.department', string='الادارة', store=True, readonly=1) department_inter_id = fields.Many2one('hr.department', string='الادارة',) city_id = fields.Many2one(related='department_inter_id.dep_city', store=True, readonly=True, string='المدينة') grade_id = fields.Many2one('salary.grid.grade', string='المرتبة', store=True, readonly=1) degree_id = fields.Many2one('salary.grid.degree', string='الدرجة', store=True, readonly=1) date_from = fields.Date(string=u'تاريخ البدء', required=1) date_to = fields.Date(string=u'تاريخ الإنتهاء', required=1) date_start = fields.Date(string=u'من') date_end = fields.Date(string=u'الى') note = fields.Text(string=u'الملاحظات', readonly=1, states={'draft': [('readonly', 0)]}) ministre_report = fields.Boolean(string=' قرار من الوزير المختص', compute='_compute_ministre_report') decision_number = fields.Char(string='رقم القرارمن الوزير المختص') decision_date = fields.Date(string='تاريخ القرارمن الوزير المختص ', default=fields.Datetime.now(), readonly=1) file_order = fields.Binary(string=' صورة القرار من الوزير المختص ', attachment=True) file_order_name = fields.Char(string=' صورة القرار من الوزير المختص ') file_decision = fields.Binary(string='نسخة من حالة الميزانية', attachment=True) file_decision_name = fields.Char(string='نسخة من حالة الميزانية') calcul_wekeend = fields.Boolean(string=' احتساب عطلة نهاية الاسبوع', default=False) lettre_number = fields.Char(string='رقم خطاب التغطية') lettre_date = fields.Date(string='تاريخ خطاب التغطية', default=fields.Datetime.now(), readonly=1) file_lettre = fields.Binary(string='نسخة خطاب التغطية', attachment=True) file_lettre_name = fields.Char(string='نسخة خطاب التغطية') report_number = fields.Char(string='عنوان التقرير') report_date = fields.Date(string='تاريخ التقرير', default=fields.Datetime.now(), readonly=1) file_report = fields.Binary(string='صورة التقرير', attachment=True) file_report_name = fields.Char(string='صورة التقرير') amount = fields.Float(string='المبلغ') transport_alocation = fields.Boolean(string='بدل نقل') net_salary = fields.Boolean(string=' الراتب') secret_report = fields.Boolean(string=' سري') anual_balance = fields.Boolean(string=' الرصيد السنوي') alowance_bonus = fields.Boolean(string=' البدلات و التعويضات و المكافات') the_availability = fields.Selection([ ('hosing_and_food', u'السكن و الطعام '), ('hosing_or_food', u'السكن أو الطعام '), ('nothing', u'لا شي '), ], string=u'السكن و الطعام ', default='hosing_and_food') type = fields.Selection([ ('internal', u' داخلى'), ('external', u' خارجى ')], string=u'إنتداب', default='internal') # city_id = fields.Many2one('res.city', string=u'المدينة') category_id = fields.Many2one('hr.deputation.category', string=u'فئة التصنيف') country_ids = fields.Many2one('hr.country.city', string=u'البلاد' ) state = fields.Selection([ ('draft', u'طلب'), ('audit', u'دراسة الطلب'), ('waiting', u'اللجنة'), ('order', u'إصدار التقرير'), ('humain', u'موارد البشرية'), ('done', u'اعتمدت'), ('finish', u'منتهية'), ('refuse', u'مرفوضة') ], string=u'الحالة', default='draft',) task_name = fields.Char(string=u' المهمة', required=1) duration = fields.Integer(string=u'المدة') member_deputation = fields.Selection([ ('member', u'انتداب عضو'), ('notmember', u'انتداب غيرعضو') ], string=u'انتداب عضو', default='notmember', required=1) deputation_type = fields.Many2one('hr.deputation.type', string='نوع الانتداب', required="1") deputation_balance_override = fields.Boolean(string=u"تجاوز رصيد الانتدابات") external_deputation_balance_override = fields.Boolean(string=u"تجاوز رصيد الانتداب الخارجي") is_paied = fields.Boolean(string='is paied', default=False) payslip_id = fields.Many2one('hr.payslip') decission_id = fields.Many2one('hr.decision', string=u'القرارات') @api.multi def open_decission_deputation(self): decision_obj= self.env['hr.decision'] if self.decission_id: decission_id = self.decission_id.id else : decision_type_id = 1 decision_date = fields.Date.today() # new date if self.deputation_type : decision_type_id = self.env.ref('smart_hr.data_employee_deputation').id decission_val={ # 'name': self.env['ir.sequence'].get('hr.deputation.seq'), 'decision_type_id':decision_type_id, 'date':decision_date, 'employee_id' :self.employee_id.id } decision = decision_obj.create(decission_val) decision.text = decision.replace_text(self.employee_id,decision_date,decision_type_id,'employee') decission_id = decision.id self.decission_id = decission_id return { 'name': _(u'قرار إنتداب'), 'view_type': 'form', 'view_mode': 'form', 'res_model': 'hr.decision', 'view_id': self.env.ref('smart_hr.hr_decision_wizard_form').id, 'type': 'ir.actions.act_window', 'res_id': decission_id, 'target': 'new' } @api.onchange('member_deputation') def onchange_member_deputation(self): res = {} if self.member_deputation == 'member': res['domain'] = {'employee_id': [('is_member', '=', True)]} else: res['domain'] = {'employee_id': [('is_member', '=', False)]} return res @api.model def get_deputation_allowance_amount(self, number_of_days): deputation_amount = 0.0 transport_amount = 0.0 deputation_allowance = False deputation_allowance_obj = self.env['hr.deputation.allowance'] employee = self.employee_id deputation_allowance_lines = deputation_allowance_obj.search([('grade_ids', 'in', [employee.grade_id.id])]) if deputation_allowance_lines: deputation_allowance = deputation_allowance_lines[0] if self.type == 'internal': if deputation_allowance.internal_transport_type == 'daily': transport_amount = deputation_allowance.internal_transport_amount * number_of_days elif deputation_allowance.internal_transport_type == 'monthly': transport_amount = deputation_allowance.internal_transport_amount / 30.0 * number_of_days if deputation_allowance.internal_deputation_type == 'daily': deputation_amount = deputation_allowance.internal_deputation_amount * number_of_days elif deputation_allowance.internal_deputation_type == 'monthly': deputation_amount = deputation_allowance.internal_deputation_amount / 30.0 * number_of_days elif self.type == 'external': if deputation_allowance.external_transport_type == 'daily': transport_amount = deputation_allowance.external_transport_amount * number_of_days elif deputation_allowance.external_transport_type == 'monthly': transport_amount = deputation_allowance.external_transport_amount / 30.0 * number_of_days # search a correct category searchs = deputation_allowance.category_ids.search([('category_id', '=', self.category_id.id)]) if searchs: if deputation_allowance.external_deputation_type == 'daily': deputation_amount = searchs[0].amount * number_of_days elif deputation_allowance.internal_transport_type == 'monthly': deputation_amount = searchs[0].amount / 30.0 * number_of_days return deputation_amount, transport_amount, deputation_allowance @api.onchange('duration') def onchange_duration(self): dep_setting = self.env['hr.deputation.setting'].search([], limit=1) warning={} if self.employee_id: if self.employee_id.deputation_balance < self.duration: warning = { 'title': _('تحذير!'), 'message': _('لقد تم تجاوز الرصيد السنوي للإنتداب!'), } self.deputation_balance_override = True if self.duration and dep_setting: if self.duration >= dep_setting.period_decision: self.ministre_report = True if self.type == 'external': external_type_id_balance = self.deputation_type.external_balance external_deputations = self.search([('type', '=', 'external'), ('employee_id', '=', self.employee_id.id), ('state', '=','done'), ('type_id', '=', self.type_id.id )]) ext_dep_duration = 0 for dep in external_deputations: ext_dep_duration += dep.duration if ext_dep_duration + self.duration > external_type_id_balance: self.external_deputation_balance_override = True return {'warning': warning} @api.one @api.depends('duration') def _compute_ministre_report(self): dep_setting = self.env['hr.deputation.setting'].search([], limit=1) if self.duration and dep_setting: if self.duration >= dep_setting.period_decision: self.ministre_report = True @api.multi def action_draft(self): self.ensure_one() # check completion of essay periode task_obj = self.env['hr.employee.task'] self.env['hr.employee.task'].create({'name': self.task_name, 'employee_id': self.employee_id.id, 'date_from': self.date_from, 'date_to': self.date_to, 'duration': self.duration, 'governmental_entity': self.governmental_entity.id, 'type_procedure': 'deputation', }) title = u" إشعار بطلب إنتداب " msg = u" إشعار بطلب إنتداب " + unicode(self.employee_id.display_name) group_id = self.env.ref('smart_hr.group_exelence_employee') self.send_exelence_group(group_id, title, msg) self.state = 'audit' def send_exelence_group(self, group_id, title, msg): ''' @param group_id: res.groups ''' for recipient in group_id.users: self.env['base.notification'].create({'title': title, 'message': msg, 'user_id': recipient.id, 'show_date': datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT), 'res_id': self.id, 'res_action': 'smart_hr.action_hr_deputation', 'type': 'hr_employee_deputation_type', }) def send_refuse_dep_group(self, group_id, title, msg): ''' @param group_id: res.groups ''' for recipient in group_id.users: self.env['base.notification'].create({'title': title, 'message': msg, 'user_id': recipient.id, 'show_date': datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT), 'res_id': self.id, 'res_action': 'smart_hr.action_hr_deputation', 'type': 'hr_employee_deputation_type', }) @api.multi def action_commission(self): for deputation in self: deputation.state = 'waiting' @api.multi def action_audit(self): for deputation in self: deputation.state = 'done' @api.multi def action_waiting(self): for deputation in self: deputation.state = 'done' @api.multi def action_done(self): self.ensure_one() self.state = 'order' @api.multi def button_refuse_audit(self): for deputation in self: title = u"' إشعار برفض الإنتداب'" msg = u"' لقد رفض الإنتداب '" + unicode(self.employee_id.display_name) + u"'" group_id = self.env.ref('smart_hr.group_deputation_department') self.send_exelence_group(group_id, title, msg) deputation.state = 'refuse' @api.multi def action_order(self): for deputation in self: deputation.state = 'humain' @api.multi def action_humain(self): for deputation in self: deputation.state = 'finish' @api.multi def button_refuse(self): for deputation in self: deputation.state = 'refuse' @api.multi def action_refuse(self): for deputation in self: deputation.state = 'refuse' @api.multi def unlink(self): for rec in self: if rec.state == 'done' : raise ValidationError(u'لا يمكن حذف الإنتداب فى هذه المرحلة يرجى مراجعة مدير النظام') return super(HrDeputation, self).unlink() @api.constrains('date_from', 'date_to') @api.onchange('date_from', 'date_to') def onchange_date_to(self): if self.date_from: if fields.Date.from_string(self.date_from).weekday() in [4, 5]: raise ValidationError(u"هناك تداخل في تاريخ البدء مع عطلة نهاية الاسبوع ") if self.env['hr.smart.utils'].public_holiday_intersection(self.date_from): raise ValidationError(u"هناك تداخل في تاريخ البدء مع عطلة او عيد ") if self.date_to: if fields.Date.from_string(self.date_to).weekday() in [4, 5]: raise ValidationError(u"هناك تداخل في تاريخ الإنتهاء مع عطلة نهاية الاسبوع") if self.env['hr.smart.utils'].public_holiday_intersection(self.date_from): raise ValidationError(u"هناك تداخل في تاريخ الإنتهاء مع عطلة او عيد ") if self.date_from and self.date_to: self.duration = self.env['hr.smart.utils'].compute_duration(self.date_from, self.date_to) @api.onchange('employee_id') def _onchange_employee_id(self): if self.employee_id: self.number = self.employee_id.number self.country_id = self.employee_id.country_id self.job_id = self.employee_id.job_id.id self.code = self.employee_id.job_id.name.number self.number_job = self.employee_id.job_id.number self.type_id = self.employee_id.type_id.id self.grade_id = self.employee_id.grade_id.id self.department_id = self.employee_id.department_id.id self.degree_id = self.employee_id.degree_id.id @api.one @api.constrains('date_from', 'date_to') def check_dates_periode(self): # Objects holiday_obj = self.env['hr.holidays'] candidate_obj = self.env['hr.candidates'] deput_obj = self.env['hr.deputation'] comm_obj = self.env['hr.employee.commissioning'] lend_obj = self.env['hr.employee.lend'] schol_obj = self.env['hr.scholarship'] termination_obj = self.env['hr.termination'] # TODO الدورات التدربية # Date validation if self.date_from > self.date_to: raise ValidationError(u"تاريخ البدء يجب ان يكون أصغر من تاريخ الإنتهاء") # check minimum request validation # التدريب search_domain = [ ('employee_id', '=', self.employee_id.id), ('state', '=', 'done'), ] for rec in candidate_obj.search(search_domain): dateto = fields.Date.from_string(rec.date_to) datefrom = fields.Date.from_string(rec.date_from) res = relativedelta(dateto, datefrom) months = res.months days = res.days # for none normal holidays test for rec in holiday_obj.search(search_domain): if rec.date_from <= self.date_from <= rec.date_to or \ rec.date_from <= self.date_to <= rec.date_to or \ self.date_from <= rec.date_from <= self.date_to or \ self.date_from <= rec.date_to <= self.date_to: raise ValidationError(u"هناك تداخل في التواريخ مع إجازة") # الإنتداب for rec in deput_obj.search(search_domain): if rec.date_from <= self.date_from <= rec.date_to or \ rec.date_from <= self.date_to <= rec.date_to or \ self.date_from <= rec.date_from <= self.date_to or \ self.date_from <= rec.date_to <= self.date_to: raise ValidationError(u"هناك تداخل في التواريخ مع إنتداب") # تكليف for rec in comm_obj.search(search_domain): if rec.date_from <= self.date_from <= rec.date_to or \ rec.date_from <= self.date_to <= rec.date_to or \ self.date_from <= rec.date_from <= self.date_to or \ self.date_from <= rec.date_to <= self.date_to: raise ValidationError(u"هناك تداخل في التواريخ مع تكليف") # إعارة for rec in lend_obj.search(search_domain): if rec.date_from <= self.date_from <= rec.date_to or \ rec.date_from <= self.date_to <= rec.date_to or \ self.date_from <= rec.date_from <= self.date_to or \ self.date_from <= rec.date_to <= self.date_to: raise ValidationError(u"هناك تداخل في التواريخ مع إعارة") # الابتعاث for rec in schol_obj.search(search_domain): if rec.date_from <= self.date_from <= rec.date_to or \ rec.date_from <= self.date_to <= rec.date_to or \ self.date_from <= rec.date_from <= self.date_to or \ self.date_from <= rec.date_to <= self.date_to: raise ValidationError(u"هناك تداخل في التواريخ مع ابتعاث") for rec in termination_obj.search(search_domain): if rec.date <= self.date_from: raise ValidationError(u"هناك تداخل في التواريخ مع طى قيد") # التدريب for rec in candidate_obj.search(search_domain): if rec.date_from <= self.date_from <= rec.date_to or \ rec.date_from <= self.date_to <= rec.date_to or \ self.date_from <= rec.date_from <= self.date_to or \ self.date_from <= rec.date_to <= self.date_to: raise ValidationError(u"هناك تداخل في التواريخ مع تدريب")
class ResPartnerEnrollSearch(models.TransientModel): _name = 'res.partner.enroll.search' rockbotic_before = fields.Boolean() item_ids = fields.One2many( comodel_name='res.partner.enroll.search.item', inverse_name='search_id') partner_id = fields.Many2one( comodel_name='res.partner', domain="[('is_company','=',False)]") parent_id = fields.Many2one( comodel_name='res.partner', domain="[('is_company','=',True)]") @api.model def default_get(self, fields_list): context = self.env.context res = super(ResPartnerEnrollSearch, self).default_get(fields_list) if context.get('active_id'): lead = self.env['crm.lead'].browse(context['active_id']) res.update({ 'rockbotic_before': lead.rockbotic_before, 'parent_id': lead.parent_id.id, 'partner_id': lead.partner_id.id, }) partner_obj = self.env['res.partner'] partners = partner_obj.search( [('name', 'ilike', lead.contact_name), ('parent_id', '!=', False), ('registered_partner', '=', 'True')]) parents = partner_obj.search( [('is_company', '=', True), '|', '|', '|', '|', ('name', 'ilike', lead.partner_name), ('vat', 'ilike', lead.vat), ('email', 'ilike', lead.email_from), ('phone', 'ilike', lead.phone), ('mobile', 'ilike', lead.phone)]) partners |= partner_obj.search( [('parent_id', 'in', parents.ids), ('registered_partner', '=', 'True')]) if not partners: contact_names = lead.contact_name.split(' ') for contact_name in contact_names: partners |= partner_obj.search( [('name', 'ilike', contact_name), ('registered_partner', '=', 'True'), ('parent_id', '!=', False)]) if contact_name else\ partner_obj parent_names = lead.partner_name.split(' ') for parent_name in parent_names: parents = partner_obj.search( [('is_company', '=', True), ('name', 'ilike', parent_name), ('parent_id', '=', False)]) if parent_name else\ partner_obj partners |= partner_obj.search( [('parent_id', 'in', parents.ids), ('registered_partner', '=', 'True')]) if partners: res.update({ 'item_ids': [(0, 0, {'partner_id': x.id, 'parent_partner_id': x.parent_id.id, 'parent_vat': x.parent_id.vat}) for x in partners], }) return res @api.constrains('item_ids', 'rockbotic_before') def _check_selected_item_ids(self): for record in self.filtered('rockbotic_before'): selected_items = record.item_ids.filtered('checked') if not selected_items: raise exceptions.ValidationError( _('Please select one line.')) elif len(selected_items) > 1: raise exceptions.ValidationError( _('There can only be one selected.')) @api.multi def action_apply_same_parent(self): if self.rockbotic_before: item = self.item_ids.filtered('checked')[:1] self.parent_id = item.parent_partner_id return self.action_apply() @api.multi def action_apply(self): context = self.env.context lead = self.env[context.get('active_model')].browse(context.get( 'active_id')) if self.rockbotic_before: item = self.item_ids.filtered('checked')[:1] partner = item.partner_id if not self.partner_id else \ self.partner_id parent_id = lead._lead_create_contact( lead, lead.partner_name, True) if \ not self.parent_id else self.parent_id.id else: parent_id = lead._lead_create_contact( lead, lead.partner_name, True) if \ not self.parent_id else self.parent_id.id partner_id = lead._lead_create_contact( lead, lead.contact_name, False, parent_id=parent_id) if not \ self.partner_id else self.partner_id.id partner = self.env['res.partner'].browse(partner_id) if partner.parent_id.id != parent_id: change_wiz = self.env['res.partner.parent.change'].create({ 'partner_id': partner.id, 'old_parent_id': partner.parent_id.id, 'new_parent_id': parent_id, }) change_wiz.change_parent_id() lead.write({ 'partner_id': partner.id, 'parent_id': parent_id, }) action = self.env.ref( 'rockbotic_website_crm.action_crm_lead2opportunity_partner') action_dict = action.read()[0] if action else {} action_dict['context'] = safe_eval( action_dict.get('context', '{}')) action_dict['context'].update({ 'active_id': context.get('active_id'), 'active_ids': context.get('active_ids'), 'active_model': context.get('active_model'), }) return action_dict
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'), ) no_facturar = fields.Boolean(string=_(u'No Facturar'), ) 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')) if not order.date_reception: raise UserError( _('This Sale Order not has Date Reception')) for line in order.order_line: if not line.route_id: raise UserError( _('Product line %s does not have a route assigned' % (line.product_id.default_code))) if line.standard_cost == 0.00: raise UserError( "No se puede validar un producto con costo 0 (%s)" % (line.product_id.default_code)) # 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')) if order.warehouse_id != order.project_id.warehouse_id: raise UserError( ('No coincide la Analítica con el almacen seleccionado')) 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 line.product_id.type == 'service': continue 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 == self.env.uid: 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.')) else: order.write({'approve': 'approved'}) order.date_approved = fields.Datetime.now() return if not self.env.user.has_group('account.group_account_manager'): raise ValidationError( _('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() resws2 = order.product_data_validation2() # 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['journal_id'] = self.project_id.journal_sale_id.id 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 qdodoo_product_mrp_line_wizard(models.TransientModel): """ 报表查询条件模型 """ _name = 'qdodoo.product.mrp.line.wizard' company_id = fields.Many2one('res.company', u'公司', required=True) start_period = fields.Many2one('account.period', u'开始账期', required=True) end_period = fields.Many2one('account.period', u'结束账期', required=True) is_draft = fields.Boolean(u'勾选是否包含未标记') @api.multi def btn_search_date(self): sql_unlink = """delete from qdodoo_product_mrp_line_report where 1=1""" self._cr.execute(sql_unlink) # 完成判断条件 domain = [('company_id', '=', self.company_id.id)] if not self.is_draft: domain.append(('move_id.state', '=', 'posted')) # 获取所有时间段内的账期 period_obj = self.env['account.period'].search([ ('company_id', '=', self.company_id.id), ('date_start', '>=', self.start_period.date_start), ('date_stop', '<=', self.end_period.date_stop) ]) if period_obj: domain.append(('period_id', 'in', period_obj.ids)) # 获取查询的科目 account_name = self.env['ir.config_parameter'].get_param( 'qdodoo.account') if not account_name: raise osv.except_osv(_(u'警告'), _(u'缺少需要的系统参数qdodoo.account!')) else: account_lst = account_name.strip(',').split(',') account_id = self.env['account.account'].search([ ('company_id', '=', self.company_id.id), ('name', 'in', account_lst) ]) if account_id: domain.append(('account_id', 'in', account_id.ids)) else: raise osv.except_osv(_(u'警告'), _(u'该公司缺少对应的科目!')) # 查询出来所有满足条件的分录明细 # 组织数据字典{产品:{部门:{总数量,总金额}}} domain_1 = domain[:] domain_1.append(('is_mrp_inventory', '=', False)) all_dict = {} for line in self.env['account.move.line'].search(domain_1): if not line.finished_goods: key = '其他' else: key = line.finished_goods # 判断辅助核算项是否已存在 if line.analytic_account_id: analytic_account_id = line.analytic_account_id else: analytic_account_id = 'tfs' if key in all_dict: if analytic_account_id in all_dict[key]: # 如果贷方大于0,统计产量 if line.credit: all_dict[key][analytic_account_id][ 'number'] = all_dict[key][analytic_account_id].get( 'number', 0) + line.quantity # 如果借方金额大于0,统计金额 if line.debit: all_dict[key][analytic_account_id][ 'money'] = all_dict[key][analytic_account_id].get( 'money', 0) + line.debit else: # 如果贷方大于0,统计产量 if line.credit: all_dict[key][analytic_account_id] = { 'number': line.quantity } # 如果借方金额大于0,统计金额 if line.debit: all_dict[key][analytic_account_id] = { 'money': line.debit } else: # 如果贷方大于0,统计产量 if line.credit: all_dict[key] = { analytic_account_id: { 'number': line.quantity } } # 如果借方金额大于0,统计金额 if line.debit: all_dict[key] = { analytic_account_id: { 'money': line.debit } } # 统计生产均摊后的金额 domain_2 = domain[:] domain_2.append(('is_mrp_inventory', '=', True)) for line in self.env['account.move.line'].search(domain_2): if not line.finished_goods: key = '其他' else: key = line.finished_goods # 判断辅助核算项是否已存在 if line.analytic_account_id: analytic_account_id = line.analytic_account_id else: analytic_account_id = 'tfs' if key in all_dict: if analytic_account_id in all_dict[key]: # 如果贷方大于0,金额为负 if line.credit: all_dict[key][analytic_account_id][ 'money'] = all_dict[key][analytic_account_id].get( 'money', 0) - line.credit # 如果借方金额大于0,金额为正 if line.debit: all_dict[key][analytic_account_id][ 'money'] = all_dict[key][analytic_account_id].get( 'money', 0) + line.debit else: # 如果贷方大于0,金额为负 if line.credit: all_dict[key][analytic_account_id] = { 'money': -line.credit } # 如果借方金额大于0,金额为正 if line.debit: all_dict[key][analytic_account_id] = { 'money': line.debit } else: # 如果贷方大于0,金额为负 if line.credit: all_dict[key] = { analytic_account_id: { 'money': -line.credit } } # 如果借方金额大于0,金额为正 if line.debit: all_dict[key] = { analytic_account_id: { 'money': line.debit } } # 创建对应的报表数据 # 收集创建数据的id ids_lst = [] for key, value in all_dict.items(): if key == '其他': name = '其他' price_unit = 0 else: name = key.name price_unit = key.list_price for key1, value1 in value.items(): if key1 == 'tfs': department = '其他' else: department = key1.name sql = """insert into qdodoo_product_mrp_line_report (name,department,qty,price_unit,month_money) VALUES ('%s','%s',%s,%s,%s) returning id""" % ( name, department, value1.get( 'number', 0), price_unit, value1.get('money', 0)) self._cr.execute(sql) return_obj = self.env.cr.fetchall() if return_obj: ids_lst.append(return_obj[0][0]) result = self.env['ir.model.data'].get_object_reference( 'qdodoo_product_mrp_line_report', 'view_tree_qdodoo_product_mrp_line_report') view_id = result and result[1] or False return { 'name': ('产量成本明细表'), 'view_type': 'form', "view_mode": 'tree', 'res_model': 'qdodoo.product.mrp.line.report', 'type': 'ir.actions.act_window', 'domain': [('id', 'in', ids_lst)], 'views': [(view_id, 'tree')], 'view_id': [view_id], }
class AccountAssetProfile(models.Model): _inherit = 'account.asset.profile' code = fields.Char( string='Code', required=True, ) account_depreciation_id = fields.Many2one( 'account.account', required=False, ) account_expense_depreciation_id = fields.Many2one( 'account.account', required=False, ) product_categ_id = fields.Many2one( 'product.category', string='Product Category', ondelete='restrict', required=True, help="Grouping of this asset category", ) account_asset_id = fields.Many2one( domain=[('type', '=', 'other'), ('user_type.for_asset', '=', True)], ) no_depreciation = fields.Boolean( string='No Depreciation', compute='_compute_no_depreciation', store=True, help="If profile type is other than normal, No Depreciation is true", ) salvage_value = fields.Float( string='Salvage Value', default=0.0, help="Default salvage value used when create asset from move line", ) profile_type = fields.Selection( [('normal', 'Normal'), ('normal_nod', 'Normal (No-Depre)'), ('ait', 'AIT'), ('auc', 'AUC'), ('lva', 'Low-Value'), ('atm', 'ATM')], string='Asset Profile Type', required=True, default='normal', ) @api.multi def name_get(self): res = [] for record in self: if record.code and record.code != '/': name = "[%s] %s" % (record.code, record.name) else: name = record.name res.append((record.id, name)) return res @api.multi @api.depends('profile_type') def _compute_no_depreciation(self): for rec in self: rec.no_depreciation = \ rec.profile_type != 'normal' and True or False @api.multi def write(self, vals): res = super(AccountAssetProfile, self).write(vals) if 'product_categ_id' in vals: Product = self.env['product.product'] for asset_profile in self: products = Product.search([ ('asset', '=', True), ('asset_profile_id', '=', asset_profile.id)]) products.write({'categ_id': asset_profile.product_categ_id.id}) return res
class AccountFiscalPosition(models.Model): _inherit = 'account.fiscal.position' name = fields.Char('Fiscal Position', size=128, required=True) fiscal_category_id = fields.Many2one('l10n_br_account.fiscal.category', 'Categoria Fiscal') fiscal_category_fiscal_type = fields.Selection( PRODUCT_FISCAL_TYPE, related='fiscal_category_id.fiscal_type', readonly=True, store=True, string='Fiscal Type') type = fields.Selection([('input', 'Entrada'), ('output', 'Saida')], 'Tipo') type_tax_use = fields.Selection([('sale', 'Sale'), ('purchase', 'Purchase'), ('all', 'All')], 'Tax Application') inv_copy_note = fields.Boolean('Copiar Observação na Nota Fiscal') asset_operation = fields.Boolean( 'Operação de Aquisição de Ativo', help="""Caso seja marcada essa opção, será incluido o IPI na base de calculo do ICMS.""") state = fields.Selection([('draft', u'Rascunho'), ('review', u'Revisão'), ('approved', u'Aprovada'), ('unapproved', u'Não Aprovada')], 'Status', readonly=True, track_visibility='onchange', select=True, default='draft') @api.multi def onchange_type(self, type): type_tax = {'input': 'purchase', 'output': 'sale'} return { 'value': { 'type_tax_use': type_tax.get(type, 'all'), 'tax_ids': False } } @api.multi def onchange_fiscal_category_id(self, fiscal_category_id=None): result = {'value': {}} if fiscal_category_id: fiscal_category = self.env[ 'l10n_br_account.fiscal.category'].browse(fiscal_category_id) result['value'].update( {'fiscal_category_fiscal_type': fiscal_category.fiscal_type}) return result @api.v7 def map_tax(self, cr, uid, fposition_id, taxes, context=None): result = [] if not context: context = {} if fposition_id and fposition_id.company_id and\ context.get('type_tax_use') in ('sale', 'all'): if context.get('fiscal_type', 'product') == 'product': company_tax_ids = self.pool.get('res.company').read( cr, uid, fposition_id.company_id.id, ['product_tax_ids'], context=context)['product_tax_ids'] else: company_tax_ids = self.pool.get('res.company').read( cr, uid, fposition_id.company_id.id, ['service_tax_ids'], context=context)['service_tax_ids'] company_taxes = self.pool.get('account.tax').browse( cr, uid, company_tax_ids, context=context) if taxes: all_taxes = taxes + company_taxes else: all_taxes = company_taxes taxes = all_taxes if not taxes: return [] if not fposition_id: return map(lambda x: x.id, taxes) for t in taxes: ok = False tax_src = False for tax in fposition_id.tax_ids: tax_src = tax.tax_src_id and tax.tax_src_id.id == t.id tax_code_src = tax.tax_code_src_id and \ tax.tax_code_src_id.id == t.tax_code_id.id if tax_src or tax_code_src: if tax.tax_dest_id: result.append(tax.tax_dest_id.id) ok = True if not ok: result.append(t.id) return list(set(result)) @api.v8 def map_tax(self, taxes): result = self.env['account.tax'].browse() if self.company_id and \ self.env.context.get('type_tax_use') in ('sale', 'all'): if self.env.context.get('fiscal_type', 'product') == 'product': company_taxes = self.company_id.product_tax_ids else: company_taxes = self.company_id.service_tax_ids if taxes: taxes |= company_taxes for tax in taxes: for t in self.tax_ids: if t.tax_src_id == tax or t.tax_code_src_id == tax.tax_code_id: if t.tax_dest_id: result |= t.tax_dest_id break else: result |= tax return result
class WizEventAppendAssistant(models.TransientModel): _inherit = 'wiz.event.append.assistant' create_account = fields.Boolean( string='Show create account', default=False) @api.onchange('partner') def onchange_partner(self): create_account = False if self.registration and self.partner: create_account = True sale_order = self.registration.event_id.sale_order if (self.partner.employee or self.registration.analytic_account or sale_order.project_id.recurring_invoices): create_account = False self.create_account = create_account @api.multi def action_append(self): self.ensure_one() event_obj = self.env['event.event'] result = super(WizEventAppendAssistant, self).action_append() if self.create_account and not self.registration: for event in event_obj.browse(self.env.context.get('active_ids')): registration = event.registration_ids.filtered( lambda x: x.partner_id.id == self.partner.id and not x.analytic_account) if registration: self._create_account_for_not_employee_from_wizard( event, registration) elif self.create_account and self.registration: self._create_account_for_not_employee_from_wizard( self.registration.event_id, self.registration) elif (not self.create_account and self.registration and self.registration.analytic_account): self.registration.analytic_account.write( self._prepare_data_for_account_not_employee( self.registration.event_id, self.registration)) return result def _create_account_for_not_employee_from_wizard( self, event, registration): account_obj = self.env['account.analytic.account'] analytic_invoice_line_obj = self.env['account.analytic.invoice.line'] vals = self._prepare_data_for_account_not_employee(event, registration) new_account = account_obj.create(vals) registration.analytic_account = new_account.id for ticket in event.event_ticket_ids: line_vals = {'analytic_account_id': new_account.id, 'name': (ticket.sale_line.name or ticket.product_id.name), 'price_unit': ticket.price, 'price_subtotal': ticket.sale_line.price_subtotal, 'product_id': ticket.product_id.id, 'quantity': ticket.sale_line.product_uom_qty, 'uom_id': (ticket.sale_line.product_uom.id or ticket.product_id.uom_id.id)} analytic_invoice_line_obj.create(line_vals) def _prepare_data_for_account_not_employee(self, event, registration): tz = self.env.user.tz if self.from_date and self.to_date: from_date = self.from_date to_date = self.to_date today = str2date(self.from_date) else: from_date = _convert_to_local_date(registration.date_start, tz=tz).date() to_date = _convert_to_local_date(registration.date_end, tz=tz).date() today = str2date(fields.Datetime.now()) recurring_next_date = "{}-{}-{}".format( today.year, today.month, calendar.monthrange(today.year, today.month)[1]) code = self.env['ir.sequence'].get( 'account.analytic.account') parent_id = event.project_id.analytic_account_id.id or False if len(event.my_task_ids) == 1: parent_id = event.my_task_ids[0].project_id.analytic_account_id.id vals = {'name': (_('Student: %s - Payer: %s') % (registration.partner_id.name, registration.partner_id.parent_id.name)), 'type': 'contract', 'date_start': from_date, 'date': to_date, 'parent_id': parent_id, 'code': code, 'partner_id': registration.partner_id.parent_id.id, 'student': registration.partner_id.id, 'recurring_invoices': True, 'recurring_next_date': recurring_next_date} if (registration.analytic_account and registration.analytic_account.recurring_next_date): old = str2date(registration.analytic_account.recurring_next_date) new = str2date(recurring_next_date) if old > new: vals['recurring_next_date'] = old if registration.event_id.sale_order: vals['sale'] = registration.event_id.sale_order.id return vals
class ProjectLifecycle(models.Model): _name = 'compassion.project.ile' _description = 'Project lifecycle event' _order = 'date desc, id 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('_get_type', 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) name = fields.Char(readonly=True) reactivation_date = fields.Date(readonly=True) project_status = fields.Selection('_get_project_status', readonly=True) @api.model def _get_type(self): return [ ('Suspension', 'Suspension'), ('Reactivation', 'Reactivation'), ('Transition', 'Transition'), ] @api.model def _get_project_status(self): return [ ('Active', 'Active'), ('Phase Out', 'Phase Out'), ('Suspended', 'Suspended'), ('Transitioned', 'Transitioned'), ] @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 MagentoProductProduct(models.Model): _name = 'magento.product.product' _inherit = 'magento.binding' _inherits = {'product.product': 'openerp_id'} _description = 'Magento Product' @api.model def product_type_get(self): return [ ('simple', 'Simple Product'), ('configurable', 'Configurable Product'), ('virtual', 'Virtual Product'), ('downloadable', 'Downloadable Product'), # XXX activate when supported # ('grouped', 'Grouped Product'), # ('bundle', 'Bundle Product'), ] openerp_id = fields.Many2one(comodel_name='product.product', string='Product', required=True, ondelete='restrict') # XXX website_ids can be computed from categories website_ids = fields.Many2many(comodel_name='magento.website', string='Websites', readonly=True) created_at = fields.Date('Created At (on Magento)') updated_at = fields.Date('Updated At (on Magento)') product_type = fields.Selection(selection='product_type_get', string='Magento Product Type', default='simple', required=True) manage_stock = fields.Selection( selection=[('use_default', 'Use Default Config'), ('no', 'Do Not Manage Stock'), ('yes', 'Manage Stock')], string='Manage Stock Level', default='use_default', required=True, ) backorders = fields.Selection( selection=[('use_default', 'Use Default Config'), ('no', 'No Sell'), ('yes', 'Sell Quantity < 0'), ('yes-and-notification', 'Sell Quantity < 0 and ' 'Use Customer Notification')], string='Manage Inventory Backorders', default='use_default', required=True, ) magento_qty = fields.Float(string='Computed Quantity', help="Last computed quantity to send " "on Magento.") no_stock_sync = fields.Boolean( string='No Stock Synchronization', required=False, help="Check this to exclude the product " "from stock synchronizations.", ) RECOMPUTE_QTY_STEP = 1000 # products at a time @api.multi def recompute_magento_qty(self): """ Check if the quantity in the stock location configured on the backend has changed since the last export. If it has changed, write the updated quantity on `magento_qty`. The write on `magento_qty` will trigger an `on_record_write` event that will create an export job. It groups the products by backend to avoid to read the backend informations for each product. """ # group products by backend backends = defaultdict(self.browse) for product in self: backends[product.backend_id] |= product for backend, products in backends.iteritems(): self._recompute_magento_qty_backend(backend, products) return True @api.multi def _recompute_magento_qty_backend(self, backend, products, read_fields=None): """ Recompute the products quantity for one backend. If field names are passed in ``read_fields`` (as a list), they will be read in the product that is used in :meth:`~._magento_qty`. """ if backend.product_stock_field_id: stock_field = backend.product_stock_field_id.name else: stock_field = 'virtual_available' location = backend.warehouse_id.lot_stock_id product_fields = ['magento_qty', stock_field] if read_fields: product_fields += read_fields self_with_location = self.with_context(location=location.id) for chunk_ids in chunks(products.ids, self.RECOMPUTE_QTY_STEP): records = self_with_location.browse(chunk_ids) for product in records.read(fields=product_fields): new_qty = self._magento_qty(product, backend, location, stock_field) if new_qty != product['magento_qty']: self.browse(product['id']).magento_qty = new_qty @api.multi def _magento_qty(self, product, backend, location, stock_field): """ Return the current quantity for one product. Can be inherited to change the way the quantity is computed, according to a backend / location. If you need to read additional fields on the product, see the ``read_fields`` argument of :meth:`~._recompute_magento_qty_backend` """ return product[stock_field]
class res_partner(models.Model): _inherit = 'res.partner' # ~ [1593] Hitta ÅF - Länkar till sociala medier social_facebook = fields.Char(string='Facebook link') social_instagram = fields.Char(string='Instagram link') social_youtube = fields.Char(string='Youtube link') social_twitter = fields.Char(string='Twitter link') brand_name = fields.Char(string='Brand Name') consume_name = fields.Char(string='Name') is_reseller = fields.Boolean(string='Show Reseller in websearch') always_searchable = fields.Boolean( string='Always searchable', help='When checked. Reseller is always searchable.') top_image = fields.Binary(string='Top Image') type = fields.Selection(selection_add=[('visit', 'Visit')]) webshop_category_ids = fields.Many2many( comodel_name='product.public.category', string='Product Categories', domain=[('website_published', '=', True)]) website_short_description = fields.Text( string='Website Partner Short Description', translate=True) webshop_pricelist = fields.Many2one(comodel_name='product.pricelist', string='Recommended Pricelist') # ~ visit_city = fields.Char(string='Visit Adress City', compute='_compute_visit_city', store=True) # ~ #env['res.partner'].search([('is_reseller', 's=', True), ('child_ids.type', '=', 'visit')])._compute_visit_city() # ~ @api.one # ~ def _compute_visit_city(self): # ~ pass # ~ _logger.warn('\n\n%s._compute_visit_city()' % self) # ~ visit = self.child_ids.filtered(lambda c: c.active == True and c.type == 'visit' and c.city) # ~ if visit: # ~ self.visit_city = visit[0].city # ~ else: # ~ self.visit_city = False #~ @api.one #~ def _is_reseller(self): #~ self.is_reseller = self.env['ir.model.data'].xmlid_to_object('reseller_dermanord.reseller_tag') in self.category_id #~ @api.model #~ def _search_is_reseller(self, operator, value): #~ resellers = self.env.search([('category_id', '=', self.env['ir.model.data'].xmlid_to_object('reseller_dermanord.reseller_tag').id)]) #~ return [('id', 'in', [r.id for r in resellers])] @api.one @api.onchange('always_searchable') def always_searchable_onchange(self): if self.always_searchable: self.is_reseller = True @api.multi def write(self, vals): if 'always_searchable' in vals: if vals.get('always_searchable'): vals['is_reseller'] = True res = super(res_partner, self).write(vals) return res @api.multi def searchable_reseller(self): self.ensure_one() # is company and customer # has tag Hudterapeut eller SPA-terapeut, and has purchased more than 10000SEK(ex.moms) in last 12 months. # has other tags and purchase more than 2000SEK(ex.moms) once in last 12 months. if not self.always_searchable: previous_is_reseller = self.is_reseller self.is_reseller = False if (self.env['res.partner.category'].search( [('name', '=', 'Hudterapeut')])[0] in self.category_id) or ( self.env['res.partner.category'].search([ ('name', '=', 'SPA-Terapeut') ])[0] in self.category_id): if sum(self.env['account.invoice'].search([ '|', ('partner_id', '=', self.id), ('partner_id.child_ids', '=', self.id), ('date_invoice', '>=', fields.Date.to_string( (date.today() - relativedelta(years=1)))) ]).mapped('amount_untaxed')) >= 10000.0: self.is_reseller = True else: if sum(self.env['account.invoice'].search([ '|', ('partner_id', '=', self.id), ('partner_id.child_ids', '=', self.id), ('date_invoice', '>=', fields.Date.to_string( (date.today() - relativedelta(years=1)))) ]).mapped('amount_untaxed')) > 2000.0: self.is_reseller = True if self.is_reseller != previous_is_reseller: # send message to responsible self.env['mail.message'].create({ 'model': 'res.partner', 'res_id': self.id, 'author_id': self.env.ref('base.partner_root').id, 'subject': _('Partner show reseller in websearch updated'), 'type': 'notification', 'body': """<p>Show in Websearch: %s → %s</p>""" % (previous_is_reseller, self.is_reseller) }) @api.model def searchable_reseller_cron(self): for partner in self.env['res.partner'].search([ ('is_company', '=', True), ('customer', '=', True) ]): partner.searchable_reseller() @api.model def highest_sales_resellers_cron(self): domain = [('date_order', '>', '%s%s' % (str(int(fields.Datetime.now()[:4]) - 1), fields.Datetime.now()[4:])), ('date_order', '<=', fields.Datetime.now()), ('partner_id.is_reseller', '=', True), ('partner_id.is_company', '=', True), ('partner_id.is_reseller', '=', True), ('partner_id.has_webshop', '=', True), ('state', '=', 'done')] sos = self.env['sale.order'].search(domain) partners = sos.mapped('partner_id') d = {} for partner in partners: d[partner.id] = sum(self.env['sale.order'].search(domain + [( 'partner_id', '=', partner.id)]).mapped('amount_total')) sorted_d = sorted(d.items(), key=lambda x: x[1], reverse=True) partner_ids = [p[0] for p in sorted_d] lst = partner_ids[:25] if len(partner_ids) > 25 else partner_ids partner_lst = [] for l in lst: p = self.env['res.partner'].browse(l) partner_lst.append([l, p.webshop_category_ids.mapped('id')]) self.env['ir.config_parameter'].set_param( key='reseller_dermanord.highest_sales_resellers', value=str(partner_lst)) @api.model def latlonbox(self, coord, dist): ''' Get a naive lat-lon-box with sides dist centerered around coordinate coord. Parameters ========== coord : tuple Tuple-like object containing latitude and longitude in decimal degrees. dist : number Approximate width and height of box in meters. Returns ======= tuple : lat_min, lat_max, lon_min, lon_max ''' # Quirk of Odoo - circ need to live here. EARTH_CIRCUMFERENCE = 2 * math.pi * 6.371e6 # [m] , naive sphere, other ellipsoids are used for maps. lat = coord[0] lon = coord[1] local_circ = EARTH_CIRCUMFERENCE * math.cos( lat * math.pi / 180) # Circumference (lon) at latitude delta_lon = dist * 360 / local_circ / 2.0 delta_lat = dist * 360 / EARTH_CIRCUMFERENCE / 2.0 return lat - delta_lat, lat + delta_lat, lon - delta_lon, lon + delta_lon @api.model def get_reseller_by_latlon(self, coord, radius=20e3, type=None): ''' Retrieve resellers based on latlon. Utilise geopy and OpenStreet-maps Nominatim. The latter is heavily rate-limited. Parameters ========== coord : tuple Tuple-like object containing latitude and longitude in decimal degrees. radius : float Approximate distance from coord to search in meters type : str (Optional) Type of contact card to use in for the geo-comparison. Returns ======= RecordSet : res.partner RecordSet of primary contacts. ''' lat_min, lat_max, lon_min, lon_max = self.latlonbox(coord, radius) domain = [ # This is not good performance-wise, but current partner table is limited. ['partner_latitude', '>', lat_min], ['partner_latitude', '<', lat_max], ['partner_longitude', '>', lon_min], ['partner_longitude', '<', lon_max] ] if not type: domain += [("is_reseller", '=', True), ('is_company', '=', True)] rs = self.env['res.partner'].sudo().search( domain) # TODO: Ways to get around this sudo. # Todo narrow down a little more with geopy distance function return rs else: domain += [('type', '=', type), ('street', '!=', '')] geo_visit_ids = [ p['id'] for p in self.sudo().search_read([('type', '=', type), ('street', '!=', '')], ['id']) ] domain = [("is_reseller", '=', True), ('is_company', '=', True), ('child_ids', "in", geo_visit_ids)] rs = self.env['res.partner'].sudo().search(domain) return rs def _geopy_dist_sort(self, type=None): ''' Return a coordinate to use for the distance sort. IE use coord from child of designated type. Defaults to self coord. ''' self.ensure_one() if type: rs = self.child_ids.filtered(lambda r: r.type == type and r.street) if rs: rs = rs[0] return rs.partner_latitude, rs.partner_longitude return self.partner_latitude, self.partner_longitude def geopy_search(self, search_query, radius, limit=32, type=None): ''' Find resellers within radius meters of search_query Parameters ========== search_query : str Search terms to use radius : float Approximate search radius/box in meters limit : int (Optional) Limit search result type : str (Optional) Use contact type for the geo comparison. The main contact is still returned. Returns ======= RecordSet : A RecordSet of all resellers within the search limit ''' nm = geopy.Nominatim(user_agent="odoo_ma") # User agent seem arbitrary # Nominatim is rate limited ~1query/s try: query_res = nm.geocode(search_query) if not query_res: return self.env['res.partner'] # Empty set except: return self.env['res.partner'] # Empty set # Only geo search around areas, not random buildings matching search terms. valid_classes = ("place", ) # Append as needed if query_res.raw["class"] not in valid_classes: return self.env['res.partner'] #else: ret = self.get_reseller_by_latlon(query_res.point, radius) ret = ret.sorted(lambda r: geodist.distance( query_res.point, r._geopy_dist_sort(type)).km) #for r in ret: # _logger.warn("Distance: {}".format(geodist.distance(query_res.point, (r.partner_latitude,r.partner_longitude)).km) ) return ret[:limit]
class AccountConfigSettings(models.TransientModel): _name = 'account.config.settings' _inherit = 'res.config.settings' company_id = fields.Many2one('res.company', string='Company', required=True, default=lambda self: self.env.user.company_id) has_default_company = fields.Boolean(readonly=True, default=lambda self: self._default_has_default_company()) expects_chart_of_accounts = fields.Boolean(related='company_id.expects_chart_of_accounts', string='This company has its own chart of accounts', help='Check this box if this company is a legal entity.') currency_id = fields.Many2one('res.currency', related='company_id.currency_id', required=True, string='Default company currency', help="Main currency of the company.") paypal_account = fields.Char(related='company_id.paypal_account', size=128, string='Paypal account', help="""Paypal account (email) for receiving online payments (credit card, etc.) If you set a paypal account, the customer will be able to pay your invoices or quotations with a button \"Pay with Paypal\" in automated emails or through the Odoo portal.""") company_footer = fields.Text(related='company_id.rml_footer', string='Bank accounts footer preview', readonly=True, help="Bank accounts as printed in the footer of each printed document") has_chart_of_accounts = fields.Boolean(string='Company has a chart of accounts') chart_template_id = fields.Many2one('account.chart.template', string='Template', domain="[('visible','=', True)]") use_anglo_saxon = fields.Boolean(string='Use Anglo-Saxon Accounting', related='company_id.anglo_saxon_accounting') code_digits = fields.Integer(string='# of Digits', related='company_id.accounts_code_digits', help="No. of digits to use for account code") tax_calculation_rounding_method = fields.Selection( [ ('round_per_line', 'Round calculation of taxes per line'), ('round_globally', 'Round globally calculation of taxes '), ], related='company_id.tax_calculation_rounding_method', string='Tax calculation rounding method', help="""If you select 'Round per line' : for each tax, the tax amount will first be computed and rounded for each PO/SO/invoice line and then these rounded amounts will be summed, leading to the total amount for that tax. If you select 'Round globally': for each tax, the tax amount will be computed for each PO/SO/invoice line, then these amounts will be summed and eventually this total tax amount will be rounded. If you sell with tax included, you should choose 'Round per line' because you certainly want the sum of your tax-included line subtotals to be equal to the total amount with taxes.""") sale_tax_id = fields.Many2one('account.tax.template', string='Default sale tax', oldname="sale_tax") purchase_tax_id = fields.Many2one('account.tax.template', string='Default purchase tax', oldname="purchase_tax") sale_tax_rate = fields.Float(string='Sales tax (%)') purchase_tax_rate = fields.Float(string='Purchase tax (%)') bank_account_code_prefix = fields.Char(string='Bank Accounts Prefix', related='company_id.bank_account_code_prefix', help='Define the code prefix for the bank accounts', oldname='bank_account_code_char') cash_account_code_prefix = fields.Char(string='Cash Accounts Prefix', related='company_id.cash_account_code_prefix', help='Define the code prefix for the cash accounts') template_transfer_account_id = fields.Many2one('account.account.template', help="Intermediary account used when moving money from a liquidity account to another") transfer_account_id = fields.Many2one('account.account', related='company_id.transfer_account_id', domain=lambda self: [('reconcile', '=', True), ('user_type_id.id', '=', self.env.ref('account.data_account_type_current_assets').id)], help="Intermediary account used when moving money from a liquidity account to another") complete_tax_set = fields.Boolean(string='Complete set of taxes', help='''This boolean helps you to choose if you want to propose to the user to encode the sales and purchase rates or use the usual m2o fields. This last choice assumes that the set of tax defined for the chosen template is complete''') fiscalyear_last_day = fields.Integer(related='company_id.fiscalyear_last_day', default=31) fiscalyear_last_month = fields.Selection([(1, 'January'), (2, 'February'), (3, 'March'), (4, 'April'), (5, 'May'), (6, 'June'), (7, 'July'), (8, 'August'), (9, 'September'), (10, 'October'), (11, 'November'), (12, 'December')], related='company_id.fiscalyear_last_month', default=12) period_lock_date = fields.Date(related='company_id.period_lock_date', help="Only users with the 'Adviser' role can edit accounts prior to and inclusive of this date") fiscalyear_lock_date = fields.Date(string="Fiscal Year lock date", related='company_id.fiscalyear_lock_date', help="No users, including Advisers, can edit accounts prior to and inclusive of this date") module_account_check_writing = fields.Boolean(string='Pay your suppliers by check', help='This allows you to check writing and printing.\n' '-This installs the module account_check_writing.') module_account_accountant = fields.Boolean(string='Full accounting features: journals, legal statements, chart of accounts, etc.', help="""If you do not check this box, you will be able to do invoicing & payments, but not accounting (Journal Items, Chart of Accounts, ...)""") module_account_asset = fields.Boolean(string='Assets management & Revenue recognition', help='Asset management: This allows you to manage the assets owned by a company or a person.' 'It keeps track of the depreciation occurred on those assets, and creates account move for those depreciation lines.\n\n' 'Revenue recognition: This allows you to manage the Revenue recognition on selling product.' 'It keeps track of the installment occurred on those revenue recognition, and creates account move for those installment lines.\n' '-This installs the module account_asset. If you do not check this box, you will be able to do invoicing & payments, ' 'but not accounting (Journal Items, Chart of Accounts, ...)') module_account_budget = fields.Boolean(string='Budget management', help='This allows accountants to manage analytic and crossovered budgets. ' 'Once the master budgets and the budgets are defined, ' 'the project managers can set the planned amount on each analytic account.\n' '-This installs the module account_budget.') module_product_email_template = fields.Boolean(string='Send products tools and information at the invoice confirmation', help='With this module, link your products to a template to send complete information and tools to your customer.\n' 'For instance when invoicing a training, the training agenda and materials will automatically be send to your customers.') module_account_bank_statement_import_ofx = fields.Boolean(string='Import of Bank Statements in .OFX Format', help='Get your bank statements from you bank and import them in Odoo in .OFX format.\n' '-that installs the module account_bank_statement_import.') module_account_bank_statement_import_qif = fields.Boolean(string='Import of Bank Statements in .QIF Format.', help='Get your bank statements from you bank and import them in Odoo in .QIF format.\n' '-that installs the module account_bank_statement_import_qif.') group_proforma_invoices = fields.Boolean(string='Allow pro-forma invoices', implied_group='account.group_proforma_invoices', help="Allows you to put invoices in pro-forma state.") default_sale_tax_id = fields.Many2one('account.tax', help="This sale tax will be assigned by default on new products.", oldname="default_sale_tax") default_purchase_tax_id = fields.Many2one('account.tax', help="This purchase tax will be assigned by default on new products.", oldname="default_purchase_tax") group_multi_currency = fields.Boolean(string='Allow multi currencies', implied_group='base.group_multi_currency', help="Allows you multi currency environment") group_analytic_accounting = fields.Boolean(string='Analytic accounting', implied_group='analytic.group_analytic_accounting', help="Allows you to use the analytic accounting.") group_check_supplier_invoice_total = fields.Boolean(string='Check the total of supplier bills', implied_group="account.group_supplier_inv_check_total") currency_exchange_journal_id = fields.Many2one('account.journal', related='company_id.currency_exchange_journal_id', string="Rate Difference Journal",) @api.model def _default_has_default_company(self): count = self.env['res.company'].search_count([]) return bool(count == 1) @api.onchange('company_id') def onchange_company_id(self): # update related fields self.currency_id = False if self.company_id: company = self.company_id self.chart_template_id = company.chart_template_id self.has_chart_of_accounts = len(company.chart_template_id) > 0 or False self.expects_chart_of_accounts = company.expects_chart_of_accounts self.currency_id = company.currency_id self.transfer_account_id = company.transfer_account_id self.paypal_account = company.paypal_account self.company_footer = company.rml_footer self.tax_calculation_rounding_method = company.tax_calculation_rounding_method self.bank_account_code_prefix = company.bank_account_code_prefix self.cash_account_code_prefix = company.cash_account_code_prefix self.code_digits = company.accounts_code_digits # update taxes ir_values = self.env['ir.values'] taxes_id = ir_values.get_default('product.template', 'taxes_id', company_id = self.company_id.id) supplier_taxes_id = ir_values.get_default('product.template', 'supplier_taxes_id', company_id = self.company_id.id) self.default_sale_tax_id = isinstance(taxes_id, list) and taxes_id[0] or taxes_id self.default_purchase_tax_id = isinstance(supplier_taxes_id, list) and supplier_taxes_id[0] or supplier_taxes_id return {} @api.onchange('chart_template_id') def onchange_chart_template_id(self): tax_templ_obj = self.env['account.tax.template'] self.complete_tax_set = self.sale_tax_id = self.purchase_tax_id = False self.sale_tax_rate = self.purchase_tax_rate = 15 if self.chart_template_id and not self.has_chart_of_accounts: # update complete_tax_set, sale_tax_id and purchase_tax_id self.complete_tax_set = self.chart_template_id.complete_tax_set if self.chart_template_id.complete_tax_set: ir_values_obj = self.env['ir.values'] # default tax is given by the lowest sequence. For same sequence we will take the latest created as it will be the case for tax created while isntalling the generic chart of account sale_tax = tax_templ_obj.search( [('chart_template_id', '=', self.chart_template_id.id), ('type_tax_use', '=', 'sale')], limit=1, order="sequence, id desc") purchase_tax = tax_templ_obj.search( [('chart_template_id', '=', self.chart_template_id.id), ('type_tax_use', '=', 'purchase')], limit=1, order="sequence, id desc") self.sale_tax_id = sale_tax self.purchase_tax_id = purchase_tax if self.chart_template_id.code_digits: self.code_digits = self.chart_template_id.code_digits if self.chart_template_id.transfer_account_id: self.template_transfer_account_id = self.chart_template_id.transfer_account_id.id if self.chart_template_id.bank_account_code_prefix: self.bank_account_code_prefix = self.chart_template_id.bank_account_code_prefix if self.chart_template_id.cash_account_code_prefix: self.cash_account_code_prefix = self.chart_template_id.cash_account_code_prefix return {} @api.onchange('sale_tax_rate') def onchange_tax_rate(self): self.purchase_tax_rate = self.sale_tax_rate or False @api.multi def set_group_multi_currency(self): ir_model = self.env['ir.model.data'] group_user = ir_model.get_object('base', 'group_user') group_product = ir_model.get_object('product', 'group_sale_pricelist') if self.group_multi_currency: group_user.write({'implied_ids': [(4, group_product.id)]}) return True @api.multi def open_company_form(self): return { 'type': 'ir.actions.act_window', 'name': 'Configure your Company', 'res_model': 'res.company', 'res_id': self.company_id.id, 'view_mode': 'form', } @api.multi def set_transfer_account(self): if self.transfer_account_id and self.transfer_account_id != self.company_id.transfer_account_id: self.company_id.write({'transfer_account_id': self.transfer_account_id.id}) @api.multi def set_product_taxes(self): """ Set the product taxes if they have changed """ ir_values_obj = self.env['ir.values'] if self.default_sale_tax_id: ir_values_obj.sudo().set_default('product.template', "taxes_id", [self.default_sale_tax_id.id], for_all_users=True, company_id=self.company_id.id) if self.default_purchase_tax_id: ir_values_obj.sudo().set_default('product.template', "supplier_taxes_id", [self.default_purchase_tax_id.id], for_all_users=True, company_id=self.company_id.id) @api.multi def set_chart_of_accounts(self): """ install a chart of accounts for the given company (if required) """ if self.chart_template_id and not self.has_chart_of_accounts and self.expects_chart_of_accounts: if self.company_id.chart_template_id and self.chart_template_id != self.company_id.chart_template_id: raise UserError(_('You can not change a company chart of account once it has been installed')) wizard = self.env['wizard.multi.charts.accounts'].create({ 'company_id': self.company_id.id, 'chart_template_id': self.chart_template_id.id, 'transfer_account_id': self.template_transfer_account_id.id, 'code_digits': self.code_digits or 6, 'sale_tax_id': self.sale_tax_id.id, 'purchase_tax_id': self.purchase_tax_id.id, 'sale_tax_rate': self.sale_tax_rate, 'purchase_tax_rate': self.purchase_tax_rate, 'complete_tax_set': self.complete_tax_set, 'currency_id': self.currency_id.id, 'bank_account_code_prefix': self.bank_account_code_prefix, 'cash_account_code_prefix': self.cash_account_code_prefix, }) wizard.execute() @api.onchange('group_analytic_accounting') def onchange_analytic_accounting(self): if self.group_analytic_accounting: self.module_account_accountant = True
class event_event(models.Model): """Event""" _name = 'event.event' _description = 'Event' _inherit = ['mail.thread', 'ir.needaction_mixin'] _order = 'date_begin' name = fields.Char(string='Name', translate=True, required=True, readonly=False, states={'done': [('readonly', True)]}) user_id = fields.Many2one('res.users', string='Responsible', default=lambda self: self.env.user, readonly=False, states={'done': [('readonly', True)]}) company_id = fields.Many2one('res.company', string='Company', change_default=True, default=lambda self: self.env['res.company']. _company_default_get('event.event'), required=False, readonly=False, states={'done': [('readonly', True)]}) organizer_id = fields.Many2one( 'res.partner', string='Organizer', default=lambda self: self.env.user.company_id.partner_id) type = fields.Many2one('event.type', string='Category', readonly=False, states={'done': [('readonly', True)]}) color = fields.Integer('Kanban Color Index') # Seats and computation seats_max = fields.Integer( string='Maximum Available Seats', oldname='register_max', readonly=True, states={'draft': [('readonly', False)]}, help= "You can for each event define a maximum registration level. If you have too much registrations you are not able to confirm your event. (put 0 to ignore this rule )" ) seats_availability = fields.Selection([('limited', 'Limited'), ('unlimited', 'Unlimited')], 'Available Seat', required=True, default='unlimited') seats_min = fields.Integer( string='Minimum Reserved Seats', oldname='register_min', readonly=True, states={'draft': [('readonly', False)]}, help= "You can for each event define a minimum registration level. If you do not enough registrations you are not able to confirm your event. (put 0 to ignore this rule )" ) seats_reserved = fields.Integer(oldname='register_current', string='Reserved Seats', store=True, readonly=True, compute='_compute_seats') seats_available = fields.Integer(oldname='register_avail', string='Available Seats', store=True, readonly=True, compute='_compute_seats') seats_unconfirmed = fields.Integer(oldname='register_prospect', string='Unconfirmed Seat Reservations', store=True, readonly=True, compute='_compute_seats') seats_used = fields.Integer(oldname='register_attended', string='Number of Participations', store=True, readonly=True, compute='_compute_seats') @api.multi @api.depends('seats_max', 'registration_ids.state', 'registration_ids.nb_register') def _compute_seats(self): """ Determine reserved, available, reserved but unconfirmed and used seats. """ # initialize fields to 0 for event in self: event.seats_unconfirmed = event.seats_reserved = event.seats_used = event.seats_available = 0 # aggregate registrations by event and by state if self.ids: state_field = { 'draft': 'seats_unconfirmed', 'open': 'seats_reserved', 'done': 'seats_used', } query = """ SELECT event_id, state, sum(nb_register) FROM event_registration WHERE event_id IN %s AND state IN ('draft', 'open', 'done') GROUP BY event_id, state """ self._cr.execute(query, (tuple(self.ids), )) for event_id, state, num in self._cr.fetchall(): event = self.browse(event_id) event[state_field[state]] += num # compute seats_available for event in self: if event.seats_max > 0: event.seats_available = event.seats_max - ( event.seats_reserved + event.seats_used) # Registration fields registration_ids = fields.One2many('event.registration', 'event_id', string='Registrations', readonly=False, states={'done': [('readonly', True)]}) count_registrations = fields.Integer(string='Registrations', compute='_count_registrations') @api.one @api.depends('registration_ids') def _count_registrations(self): self.count_registrations = len(self.registration_ids) # Date fields date_tz = fields.Selection('_tz_get', string='Timezone', default=lambda self: self.env.user.tz) date_begin = fields.Datetime(string='Start Date', required=True, readonly=True, states={'draft': [('readonly', False)]}) date_end = fields.Datetime(string='End Date', required=True, readonly=True, states={'draft': [('readonly', False)]}) date_begin_located = fields.Datetime(string='Start Date Located', compute='_compute_date_begin_tz') date_end_located = fields.Datetime(string='End Date Located', compute='_compute_date_end_tz') @api.model def _tz_get(self): return [(x, x) for x in pytz.all_timezones] @api.one @api.depends('date_tz', 'date_begin') def _compute_date_begin_tz(self): if self.date_begin: self_in_tz = self.with_context(tz=(self.date_tz or 'UTC')) date_begin = fields.Datetime.from_string(self.date_begin) self.date_begin_located = fields.Datetime.to_string( fields.Datetime.context_timestamp(self_in_tz, date_begin)) else: self.date_begin_located = False @api.one @api.depends('date_tz', 'date_end') def _compute_date_end_tz(self): if self.date_end: self_in_tz = self.with_context(tz=(self.date_tz or 'UTC')) date_end = fields.Datetime.from_string(self.date_end) self.date_end_located = fields.Datetime.to_string( fields.Datetime.context_timestamp(self_in_tz, date_end)) else: self.date_end_located = False state = fields.Selection( [('draft', 'Unconfirmed'), ('cancel', 'Cancelled'), ('confirm', 'Confirmed'), ('done', 'Done')], string='Status', default='draft', readonly=True, required=True, copy=False, help= "If event is created, the status is 'Draft'. If event is confirmed for the particular dates the status is set to 'Confirmed'. If the event is over, the status is set to 'Done'. If event is cancelled the status is set to 'Cancelled'." ) auto_confirm = fields.Boolean(string='Auto Confirmation Activated', compute='_compute_auto_confirm') @api.one def _compute_auto_confirm(self): self.auto_confirm = self.env['ir.values'].get_default( 'marketing.config.settings', 'auto_confirmation') # Mailing email_registration_id = fields.Many2one( 'email.template', string='Registration Confirmation Email', domain=[('model', '=', 'event.registration')], help= 'This field contains the template of the mail that will be automatically sent each time a registration for this event is confirmed.' ) email_confirmation_id = fields.Many2one( 'email.template', string='Event Confirmation Email', domain=[('model', '=', 'event.registration')], help= "If you set an email template, each participant will receive this email announcing the confirmation of the event." ) reply_to = fields.Char( string='Reply-To Email', readonly=False, states={'done': [('readonly', True)]}, help= "The email address of the organizer is likely to be put here, with the effect to be in the 'Reply-To' of the mails sent automatically at event or registrations confirmation. You can also put the email address of your mail gateway if you use one." ) address_id = fields.Many2one( 'res.partner', string='Location', default=lambda self: self.env.user.company_id.partner_id, readonly=False, states={'done': [('readonly', True)]}) country_id = fields.Many2one('res.country', string='Country', related='address_id.country_id', store=True, readonly=False, states={'done': [('readonly', True)]}) description = fields.Html(string='Description', oldname='note', translate=True, readonly=False, states={'done': [('readonly', True)]}) @api.multi @api.depends('name', 'date_begin', 'date_end') def name_get(self): result = [] for event in self: dates = [ dt.split(' ')[0] for dt in [event.date_begin, event.date_end] if dt ] dates = sorted(set(dates)) result.append( (event.id, '%s (%s)' % (event.name, ' - '.join(dates)))) return result @api.one @api.constrains('seats_max', 'seats_available') def _check_seats_limit(self): if self.seats_max and self.seats_available < 0: raise Warning(_('No more available seats.')) @api.one @api.constrains('date_begin', 'date_end') def _check_closing_date(self): if self.date_end < self.date_begin: raise Warning( _('Closing Date cannot be set before Beginning Date.')) @api.model def create(self, vals): res = super(event_event, self).create(vals) if res.auto_confirm: res.confirm_event() return res @api.one def button_draft(self): self.state = 'draft' @api.one def button_cancel(self): for event_reg in self.registration_ids: if event_reg.state == 'done': raise Warning( _("You have already set a registration for this event as 'Attended'. Please reset it to draft if you want to cancel this event." )) self.registration_ids.write({'state': 'cancel'}) self.state = 'cancel' @api.one def button_done(self): self.state = 'done' @api.one def confirm_event(self): if self.email_confirmation_id: # send reminder that will confirm the event for all the people that were already confirmed regs = self.registration_ids.filtered(lambda reg: reg.state not in ('draft', 'cancel')) regs.mail_user_confirm() self.state = 'confirm' @api.one def button_confirm(self): """ Confirm Event and send confirmation email to all register peoples """ self.confirm_event() @api.one def subscribe_to_event(self): """ Subscribe the current user to a given event """ user = self.env.user num_of_seats = int(self._context.get('ticket', 1)) regs = self.registration_ids.filtered(lambda reg: reg.user_id == user) # the subscription is done as SUPERUSER_ID because in case we share the # kanban view, we want anyone to be able to subscribe if not regs: regs = regs.sudo().create({ 'event_id': self.id, 'email': user.email, 'name': user.name, 'user_id': user.id, 'nb_register': num_of_seats, }) else: regs.write({'nb_register': num_of_seats}) if regs._check_auto_confirmation(): regs.sudo().confirm_registration() @api.one def unsubscribe_to_event(self): """ Unsubscribe the current user from a given event """ # the unsubscription is done as SUPERUSER_ID because in case we share # the kanban view, we want anyone to be able to unsubscribe user = self.env.user regs = self.sudo().registration_ids.filtered( lambda reg: reg.user_id == user) regs.button_reg_cancel() @api.onchange('type') def _onchange_type(self): if self.type: self.reply_to = self.type.default_reply_to self.email_registration_id = self.type.default_email_registration self.email_confirmation_id = self.type.default_email_event self.seats_min = self.type.default_registration_min self.seats_max = self.type.default_registration_max @api.multi def action_event_registration_report(self): res = self.env['ir.actions.act_window'].for_xml_id( 'event', 'action_report_event_registration') res['context'] = { "search_default_event_id": self.id, "group_by": ['event_date:day'], } return res
class ResCompany(models.Model): _inherit = "res.company" #TODO check all the options/fields are in the views (settings + company form view) #TODO: add a cash_register_code_char for allowing cash journals' accounts having a different numbering than bank journals' accounts. fiscalyear_last_day = fields.Integer(default=31, required=True) fiscalyear_last_month = fields.Selection([(1, 'January'), (2, 'February'), (3, 'March'), (4, 'April'), (5, 'May'), (6, 'June'), (7, 'July'), (8, 'August'), (9, 'September'), (10, 'October'), (11, 'November'), (12, 'December')], default=12, required=True) period_lock_date = fields.Date( help= "Only users with the 'Adviser' role can edit accounts prior to and inclusive of this date" ) fiscalyear_lock_date = fields.Date( string="Fiscal Year lock date", help= "No users, including Advisers, can edit accounts prior to and inclusive of this date" ) transfer_account_id = fields.Many2one( 'account.account', domain=lambda self: [ ('reconcile', '=', True), ('user_type_id.id', '=', self.env.ref('account.data_account_type_current_assets').id), ('deprecated', '=', False) ], string="Transfer Account", help= "Intermediary account used when moving money from a liquidity account to another" ) expects_chart_of_accounts = fields.Boolean( string='Expects a Chart of Accounts', default=True) chart_template_id = fields.Many2one( 'account.chart.template', help='The chart template for the company (if any)') bank_account_code_char = fields.Char( string='Code of the main bank account') accounts_code_digits = fields.Integer( string='Number of digits in an account code') tax_calculation_rounding_method = fields.Selection( [ ('round_per_line', 'Round per Line'), ('round_globally', 'Round Globally'), ], default='round_per_line', string='Tax Calculation Rounding Method', help= "If you select 'Round per Line' : for each tax, the tax amount will first be computed and rounded for each PO/SO/invoice line and then these rounded amounts will be summed, leading to the total amount for that tax. If you select 'Round Globally': for each tax, the tax amount will be computed for each PO/SO/invoice line, then these amounts will be summed and eventually this total tax amount will be rounded. If you sell with tax included, you should choose 'Round per line' because you certainly want the sum of your tax-included line subtotals to be equal to the total amount with taxes." ) paypal_account = fields.Char( string='Paypal Account', size=128, help="Paypal username (usually email) for receiving online payments.") currency_exchange_journal_id = fields.Many2one( 'account.journal', string="Currency Adjustments Journal", domain=[('type', '=', 'general')]) income_currency_exchange_account_id = fields.Many2one( 'account.account', related='currency_exchange_journal_id.default_credit_account_id', string="Gain Exchange Rate Account", domain= "[('internal_type', '=', 'other'), ('deprecated', '=', False), ('company_id', '=', id)]" ) expense_currency_exchange_account_id = fields.Many2one( 'account.account', related='currency_exchange_journal_id.default_debit_account_id', string="Loss Exchange Rate Account", domain= "[('internal_type', '=', 'other'), ('deprecated', '=', False), ('company_id', '=', id)]" ) anglo_saxon_accounting = fields.Boolean( string="Use anglo-saxon accounting") property_stock_account_input_categ_id = fields.Many2one( 'account.account', oldname="property_stock_account_input_categ") property_stock_account_output_categ_id = fields.Many2one( 'account.account', oldname="property_stock_account_output_categ") property_stock_valuation_account_id = fields.Many2one('account.account') @api.multi def compute_fiscalyear_dates(self, date): """ Computes the start and end dates of the fiscalyear where the given 'date' belongs to @param date: a datetime object @returns: a dictionary with date_from and date_to """ self = self[0] last_month = self.fiscalyear_last_month last_day = self.fiscalyear_last_day if (date.month < last_month or (date.month == last_month and date.date <= last_day)): date = date.replace(month=last_month, day=last_day) else: date = date.replace(month=last_month, day=last_day, year=date.year + 1) date_to = date date_from = date + timedelta(days=1) date_from = date_from.replace(year=date_from.year - 1) return {'date_from': date_from, 'date_to': date_to} def get_new_account_code(self, code, old_prefix, new_prefix, digits): new_prefix_length = len(new_prefix) number = str(int(code[len(old_prefix):])) code = new_prefix + str( '0' * (digits - new_prefix_length - len(number))) + number return code @api.multi def write(self, values): # Repercute the change on accounts if values.get('bank_account_code_char', False) or values.get( 'accounts_code_digits', False): bank_code = values.get('bank_account_code_char', False) or self.bank_account_code_char digits = values.get('accounts_code_digits', False) or self.accounts_code_digits accounts = self.env['account.account'].search( [('code', 'like', self.bank_account_code_char), ('internal_type', '=', 'liquidity'), ('company_id', '=', self.id)], order='code asc') for account in accounts: if account.code.startswith(self.bank_account_code_char): account.write({ 'code': self.get_new_account_code(account.code, self.bank_account_code_char, bank_code, digits) }) return super(ResCompany, self).write(values)
class MagentoStoreview(models.Model): _name = 'magento.storeview' _inherit = ['magento.binding', 'magento.config.specializer'] _description = "Magento Storeview" _parent_name = 'store_id' _order = 'sort_order ASC, id ASC' name = fields.Char(required=True, readonly=True) code = fields.Char(readonly=True) enabled = fields.Boolean(string='Enabled', readonly=True) sort_order = fields.Integer(string='Sort Order', readonly=True) store_id = fields.Many2one(comodel_name='magento.store', string='Store', ondelete='cascade', readonly=True) lang_id = fields.Many2one(comodel_name='res.lang', string='Language') section_id = fields.Many2one(comodel_name='crm.case.section', string='Sales Team') backend_id = fields.Many2one( comodel_name='magento.backend', related='store_id.website_id.backend_id', string='Magento Backend', store=True, readonly=True, # override 'magento.binding', can't be INSERTed if True: required=False, ) import_orders_from_date = fields.Datetime( string='Import sale orders from date', help='do not consider non-imported sale orders before this date. ' 'Leave empty to import all sale orders', ) no_sales_order_sync = fields.Boolean( string='No Sales Order Synchronization', help='Check if the storeview is active in Magento ' 'but its sales orders should not be imported.', ) catalog_price_tax_included = fields.Boolean(string='Prices include tax') @api.multi def import_sale_orders(self): session = ConnectorSession(self.env.cr, self.env.uid, context=self.env.context) import_start_time = datetime.now() for storeview in self: if storeview.no_sales_order_sync: _logger.debug("The storeview '%s' is active in Magento " "but is configured not to import the " "sales orders", storeview.name) continue backend_id = storeview.backend_id.id if storeview.import_orders_from_date: from_string = fields.Datetime.from_string from_date = from_string(storeview.import_orders_from_date) else: from_date = None sale_order_import_batch.delay( session, 'magento.sale.order', backend_id, {'magento_storeview_id': storeview.magento_id, 'from_date': from_date, 'to_date': import_start_time}, priority=1) # executed as soon as possible # Records from Magento are imported based on their `created_at` # date. This date is set on Magento at the beginning of a # transaction, so if the import is run between the beginning and # the end of a transaction, the import of a record may be # missed. That's why we add a small buffer back in time where # the eventually missed records will be retrieved. This also # means that we'll have jobs that import twice the same records, # but this is not a big deal because the sales orders will be # imported the first time and the jobs will be skipped on the # subsequent imports next_time = import_start_time - timedelta(seconds=IMPORT_DELTA_BUFFER) next_time = fields.Datetime.to_string(next_time) self.write({'import_orders_from_date': next_time}) return True
class PurchaseOrder(models.Model): _inherit = 'purchase.order' invoice_method = fields.Selection( selection_add=[('invoice_plan', 'Invoice Plan')], ) use_invoice_plan = fields.Boolean( string='Use Invoice Plan', default=False, readonly=True, states={'draft': [('readonly', False)]}, ) invoice_plan_ids = fields.One2many( 'purchase.invoice.plan', 'order_id', string='Invoice Plan', copy=True, readonly=True, states={'draft': [('readonly', False)]}, ) invoice_plan_wd_ids = fields.One2many( 'purchase.invoice.plan', 'order_id', string='Invoice Plan with Advance', copy=True, ) use_advance = fields.Boolean( string='Advance on 1st Invoice', readonly=True, ) use_deposit = fields.Boolean( string='Deposit on 1st Invoice', readonly=True, ) invoice_mode = fields.Selection( [('change_price', 'As 1 Job (change price)'), ('change_quantity', 'As Units (change quantity)')], string='Invoice Mode', requied=True, readonly=True, ) plan_invoice_created = fields.Boolean( string='Invoice Created', compute='_compute_plan_invoice_created', store=True, help="Compute whether number of invoices " "(not cancelled invoices) are created as planned", ) total_invoice_amount = fields.Float( compute='_compute_plan_invoice_created', string='Invoice Amount', ) num_installment = fields.Integer( string='Number of Installment', default=0, ) @api.one @api.depends('invoice_ids.state') def _compute_plan_invoice_created(self): if not self.invoice_ids: self.plan_invoice_created = False else: total_invoice_amt = 0 num_valid_invoices = 0 for i in self.invoice_ids: if i.state not in ('cancel'): num_valid_invoices += 1 if i.state in ('open', 'paid'): total_invoice_amt += i.amount_untaxed num_plan_invoice = len(list(set([i.installment for i in self.invoice_plan_ids]))) self.plan_invoice_created = num_valid_invoices == num_plan_invoice self.total_invoice_amount = total_invoice_amt @api.model def _calculate_subtotal(self, vals): if vals.get('invoice_plan_ids', False) or \ vals.get('invoice_plan_wd_ids', False): plan_ids = self.invoice_plan_ids or self.invoice_plan_wd_ids old_installment = 0 subtotal = 0.0 index_list = [] # Keep the subtotal line index i = 0 for line in plan_ids: if line.installment > old_installment: # Start new installment if len(index_list) > 0: del index_list[-1] # Remove last index subtotal = 0.0 index_list.append(i) if line.installment == 0: line.subtotal = 0.0 else: subtotal += line.invoice_amount line.subtotal = subtotal old_installment = line.installment i += 1 if len(index_list) > 0: del index_list[-1] # Now, delete subtotal not in the index_list for i in index_list: self.invoice_plan_ids[i].subtotal = 0.0 @api.model def _validate_invoice_plan(self, vals): if vals.get('invoice_plan_ids', False): for line in vals.get('invoice_plan_ids'): # Deleting (2) line that is not being cancelled plan = self.env['purchase.invoice.plan'].browse(line[1]) if line[0] == 2: # Deletion plan = self.env['purchase.invoice.plan'].browse(line[1]) if plan.state and plan.state != 'cancel': raise except_orm( _('Delete Error!'), _("You are trying deleting line(s) " "that has not been cancelled!\n" "Please discard change and try again!")) @api.onchange('invoice_method') def _onchange_invoice_method(self): self.use_invoice_plan = self.invoice_method == 'invoice_plan' @api.onchange('use_invoice_plan') def _onchange_use_invoice_plan(self): if self.use_invoice_plan: self.invoice_method = 'invoice_plan' else: default_invoice_method = self.env['ir.values'].get_default( 'purchase.order', 'invoice_method') self.invoice_method = default_invoice_method or 'order' # Don't know why, but can't use v8 API !!, so revert to v7 def copy(self, cr, uid, id, default=None, context=None): default = default or {} order_id = super(PurchaseOrder, self).\ copy(cr, uid, id, default, context) order = self.browse(cr, uid, order_id) for invoice_plan in order.invoice_plan_ids: copy_to_line_id = invoice_plan.order_line_id and \ invoice_plan.order_line_id.copy_to_line_id.id self.pool.get('purchase.invoice.plan').write( cr, uid, [invoice_plan.id], {'order_line_id': copy_to_line_id}, context) return order_id @api.multi def write(self, vals): for purchase in self: purchase._validate_invoice_plan(vals) res = super(PurchaseOrder, self).write(vals) for purchase in self: purchase._calculate_subtotal(vals) self.env['purchase.invoice.plan']._validate_installment_date( self.invoice_plan_ids) return res @api.one def _check_invoice_plan(self): if self.invoice_method == 'invoice_plan': # if self.invoice_mode == 'change_price': # for order_line in self.order_line: # if order_line.product_qty != 1: # raise UserError( # _('For invoice plan mode "As 1 Job", ' # 'all line quantity must equal to 1')) obj_precision = self.env['decimal.precision'] prec = obj_precision.precision_get('Account') for order_line in self.order_line: subtotal = order_line.price_subtotal invoice_lines = self.env['purchase.invoice.plan'].search( [('order_line_id', '=', order_line.id)]) total_amount = 0.0 for line in invoice_lines: total_amount += line.invoice_amount # Validate percent if round(line.invoice_percent / 100 * subtotal, prec) != \ round(line.invoice_amount, prec): raise except_orm( _('Invoice Plan Percent Mismatch!'), _("%s on installment %s") % (order_line.name, line.installment)) if round(total_amount, prec) != round(subtotal, prec): raise except_orm( _('Invoice Plan Amount Mismatch!'), _("%s, plan amount %d not equal to line amount %d!") % (order_line.name, total_amount, subtotal)) return True @api.multi def _create_deposit_invoice(self, percent, amount, date_invoice=False, blines=False): for order in self: if amount: filter_values = {'date_invoice': date_invoice} if blines.is_advance_installment: advance_label = _('Advance') filter_values.update({'is_advance': True, }) elif blines.is_deposit_installment: advance_label = _('Deposit') filter_values.update({'is_deposit': True, }) company = self.env.user.company_id account_id = company.account_deposit_supplier.id name = _("%s of %s %%") % (advance_label, percent) # create the invoice inv_line_values = { 'name': name, 'origin': order.name, 'user_id': self.env.user.id, 'account_id': account_id, 'price_unit': amount, 'quantity': 1.0, 'discount': False, 'uos_id': False, 'product_id': False, 'invoice_line_tax_id': [ (6, 0, [x.id for x in order.order_line[0].taxes_id])], } inv_values = self._prepare_invoice(order, inv_line_values) inv_values.update(filter_values) # Chainging from [6, 0, ...] to [0, 0, ...] inv_values['invoice_line'] = [ (0, 0, inv_values['invoice_line'][0][2])] invoice = self.env['account.invoice'].create(inv_values) # compute the invoice invoice.button_compute(set_total=True) # Link this new invoice to related purchase order order.write({'invoice_ids': [(4, invoice.id)]}) if date_invoice: data = invoice.onchange_payment_term_date_invoice( order.payment_term_id.id, date_invoice) else: data = invoice.onchange_payment_term_date_invoice( order.payment_term_id.id, time.strftime(DEFAULT_SERVER_DATE_FORMAT)) if data.get('value', False): invoice.write(data['value']) return invoice.id return False @api.model def compute_advance_payment_line(self, inv_id): invoice = self.env['account.invoice'].browse(inv_id) order = self for advance in order.invoice_ids: if advance.state != 'cancel' and \ advance.is_advance: for preline in advance.invoice_line: ratio = (order.amount_untaxed and (invoice.amount_untaxed / order.amount_untaxed) or 1.0) inv_line = preline.copy( {'invoice_id': inv_id, 'price_unit': -preline.price_unit * ratio}) inv_line.quantity = 1.0 invoice.button_compute() return True @api.model def compute_deposit_line(self, inv_id): invoice = self.env['account.invoice'].browse(inv_id) order = self for deposit in order.invoice_ids: if deposit.state != 'cancel' and \ deposit.is_deposit: for dline in deposit.invoice_line: dline.copy({'invoice_id': inv_id, 'price_unit': -1 * dline.price_unit}) invoice.button_compute() return True @api.multi def wkf_approve_order(self): self._check_invoice_plan() return super(PurchaseOrder, self).wkf_approve_order() @api.multi def action_invoice_create(self): self._check_invoice_plan() if self.plan_invoice_created: raise UserError(_('Create more invoices not allowed!')) invoice_ids = [] # Case use_invoice_plan, create multiple invoice by installment for order in self: if order.invoice_method == 'invoice_plan': plan_obj = self.env['purchase.invoice.plan'] installments = list(set([plan.installment for plan in order.invoice_plan_ids])) last_installment = max(installments) for installment in installments: # Getting invoice plan for each installment blines = plan_obj.search( [('installment', '=', installment), ('order_id', '=', order.id), ('state', 'in', [False, 'cancel'])]) if blines: if installment == 0: if blines.is_advance_installment or \ blines.is_deposit_installment: inv_id = self._create_deposit_invoice( blines.deposit_percent, blines.deposit_amount, blines.date_invoice, blines) invoice_ids.append(inv_id) blines.write({'ref_invoice_id': inv_id}) invoice = self.env['account.invoice'].\ browse(inv_id) invoice.write({ 'date_invoice': blines.date_invoice }) else: percent_dict = {} date_invoice = (blines and blines[0].date_invoice or False) for b in blines: percent_dict.update( {b.order_line_id: b.invoice_percent}) order = order.with_context( installment=installment, invoice_plan_percent=percent_dict, date_invoice=date_invoice) inv_id = super(PurchaseOrder, order).\ action_invoice_create() invoice_ids.append(inv_id) blines.write({'ref_invoice_id': inv_id}) invoice = self.env['account.invoice'].\ browse(inv_id) invoice.write({'date_invoice': date_invoice}) for line in invoice.invoice_line: # Remove line with negative price line if line.price_unit < 0: line.unlink() order.compute_advance_payment_line(inv_id) if installment == last_installment: order.compute_deposit_line(inv_id) else: inv_id = super(PurchaseOrder, order).action_invoice_create() invoice_ids.append(inv_id) order._action_invoice_create_hook(invoice_ids) # Special Hook return inv_id @api.model def _action_invoice_create_hook(self, invoice_ids): # For Hook return @api.model def _prepare_inv_line(self, account_id, order_line): # Call super res = super(PurchaseOrder, self).\ _prepare_inv_line(account_id, order_line) # For invoice plan invoice_plan_percent = self._context.get('invoice_plan_percent', False) if invoice_plan_percent: if order_line in invoice_plan_percent: if order_line.order_id.invoice_mode == 'change_quantity': res.update({'quantity': (res.get('quantity') or 0.0) * (order_line and invoice_plan_percent[order_line] or 0.0) / 100}) elif order_line.order_id.invoice_mode == 'change_price': res.update({'price_unit': (res.get('price_unit') or 0.0) * (res.get('quantity') or 0.0) * (order_line and invoice_plan_percent[order_line] or 0.0) / 100, 'quantity': 1.0}) else: return {} return res @api.multi def action_cancel_draft_invoices(self): assert len(self) == 1, \ 'This option should only be used for a single id at a time.' # Get all unpaid invoice for invoice in self.invoice_ids: if invoice.state in ('draft',): invoice.signal_workflow('invoice_cancel') return True
class AccountAsset(ChartFieldAction, models.Model): _name = 'account.asset' _inherit = ['account.asset', 'mail.thread'] name = fields.Char( default='/', readonly=False, states={}, # Always editable ) parent_id = fields.Many2one( readonly=False, states={}, # Procurement team want it to always editable. ) type = fields.Selection( # Need this way of doing default, because default_type in context will # cause problem compute depreciation table, it set line type wrongly default=lambda self: self._context.get('type') or 'normal', index=True, ) profile_type = fields.Selection( [('normal', 'Normal'), ('normal_nod', 'Normal (No-Depre)'), ('ait', 'AIT'), ('auc', 'AUC'), ('lva', 'Low-Value'), ('atm', 'ATM')], related='profile_id.profile_type', string='Asset Profile Type', store=True, readonly=True, ) status = fields.Many2one( 'account.asset.status', string='Asset Status', default=lambda self: self.env.ref('pabi_asset_management.' 'asset_status_cancel'), domain="[('map_state', '=', state)]", required=False, index=True, help="Status vs State\n" "Draft → ยกเลิก\n" "Running → ใช้งานปกติ, ส่งมอบ, โอนเป็นครุภัณฑ์, ชำรุด, รอจำหน่าย\n" "Removed → จำหน่าย, สูญหาย\n" "Close → หมดอายุการใช้งาน" ) status_code = fields.Char( string='Status Code', related='status.code', readonly=True, store=True, ) deliver_to = fields.Char( string='Deliver to', help="If status is chagned to 'delivery', this field is required", ) deliver_date = fields.Date( string='Delivery date', help="If status is chagned to 'delivery', this field is required", ) code = fields.Char( string='Code', # Rename default='/', ) code2 = fields.Char( string='Code (legacy)', help="Code in Legacy System", ) product_id = fields.Many2one( 'product.product', string='Asset Type', domain=[('asset_profile_id', '!=', False)], readonly=True, states={'draft': [('readonly', False)]}, help="This asset is created from this product class", ) move_id = fields.Many2one( 'stock.move', string='Move', readonly=True, ) picking_id = fields.Many2one( 'stock.picking', string='Picking', related='move_id.picking_id', store=True, readonly=True, ) adjust_id = fields.Many2one( 'account.asset.adjust', string='Adjustment', readonly=True, copy=False, help="For asset that is created from the asset type adjustment", ) date_picking = fields.Datetime( string='Picking Date', related='move_id.picking_id.date_done', readonly=True, ) purchase_id = fields.Many2one( 'purchase.order', string='Purchase Order', related='move_id.purchase_line_id.order_id', store=True, readonly=True, ) uom_id = fields.Many2one( 'product.uom', string='Unit of Measure', related='move_id.product_uom', store=True, readonly=True, ) no_depreciation = fields.Boolean( string='No Depreciation', related='profile_id.no_depreciation', readonly=True, ) # Additional Info asset_purchase_method_id = fields.Many2one( 'asset.purchase.method', string='Purchase Method', ) purchase_request_id = fields.Many2one( 'purchase.request', string='Purchase Request', related='move_id.purchase_line_id.quo_line_id.requisition_line_id.' 'purchase_request_lines.request_id', readonly=True, help="PR of this asset", ) pr_requester_id = fields.Many2one( 'res.users', string='PR Requester', related='purchase_request_id.requested_by', readonly=True, help="PR Requester of this asset", ) date_request = fields.Date( string='PR Approved Date', related='move_id.purchase_line_id.quo_line_id.requisition_line_id.' 'purchase_request_lines.request_id.date_approve', readonly=True, help="PR's Approved Date", ) doc_request_id = fields.Many2one( 'account.asset.request', string='Asset Request', readonly=True, ) responsible_user_id = fields.Many2one( 'res.users', string='Responsible Person', readonly=True, states={'draft': [('readonly', False)]}, ) owner_project_id = fields.Many2one( 'res.project', string='Project', readonly=True, help="Owner project of the budget structure", ) owner_section_id = fields.Many2one( 'res.section', string='Section', readonly=True, help="Owner section of the budget structure", ) purchase_value = fields.Float( default=0.0, # to avoid false ) requester_id = fields.Many2one( 'res.users', string='Requester', readonly=True, states={'draft': [('readonly', False)]}, ) building_id = fields.Many2one( 'res.building', string='Building', readonly=True, states={'draft': [('readonly', False)]}, ondelete='restrict', ) floor_id = fields.Many2one( 'res.floor', string='Floor', readonly=True, states={'draft': [('readonly', False)]}, ondelete='restrict', ) room_id = fields.Many2one( 'res.room', string='Room', readonly=True, states={'draft': [('readonly', False)]}, ) serial_number = fields.Char( string='Serial Number', readonly=False, ) warranty_start_date = fields.Date( string='Warranty Start Date', default=lambda self: fields.Date.context_today(self), track_visibility='onchange', readonly=False, ) warranty_expire_date = fields.Date( string='Warranty Expire Date', default=lambda self: fields.Date.context_today(self), track_visibility='onchange', readonly=False, ) # Transfer Asset target_asset_ids = fields.Many2many( 'account.asset', 'account_asset_source_target_rel', 'source_asset_id', 'target_asset_id', string='To Asset(s)', domain=['|', ('active', '=', True), ('active', '=', False)], help="In case of transfer, this field show asset created by this one", readonly=True, ) target_asset_count = fields.Integer( string='Source Asset Count', compute='_compute_asset_count', ) source_asset_ids = fields.Many2many( 'account.asset', 'account_asset_source_target_rel', 'target_asset_id', 'source_asset_id', string='From Asset(s)', domain=['|', ('active', '=', True), ('active', '=', False)], help="List of source asset that has been transfer to this one", ) source_asset_count = fields.Integer( string='Source Asset Count', compute='_compute_asset_count', ) image = fields.Binary( string='Image', ) repair_note_ids = fields.One2many( 'asset.repair.note', 'asset_id', string='Repair Notes', ) depreciation_summary_ids = fields.One2many( 'account.asset.depreciation.summary', 'asset_id', string='Depreciation Summary', readonly=True, ) parent_type = fields.Selection( [('ait', 'AIT'), ('auc', 'AUC'), ('atm', 'ATM'), ], string='Parent Type', default='atm', ) installment = fields.Integer( string='Installment', readonly=True, help="Installment, if related to PO's invoice plan", ) num_installment = fields.Integer( string='Number of Installment', readonly=True, help="Total Installment, if related to PO's invoice plan", ) installment_str = fields.Char( string='Installment', compute='_compute_installment_str', help="Nicely format installment vs number of installment", ) total_child_value = fields.Float( string='Total Value', compute='_compute_total_child_value', help="Sum of this parent's child purchase values", ) child_asset_count = fields.Integer( string='Child Asset Count', compute='_compute_asset_count', ) # Special field that search PO through adjustmnet and po all_purchase = fields.Char( string='All Purchase Orders', help="Search POs from purchase_id and " "adjust_id.invoice_id.expense_id.ship_purchase_id", ) manual = fields.Boolean( string='Excel Import', compute='_compute_manual', help="True, if any line imported manually by excel." ) _sql_constraints = [('code_uniq', 'unique(code)', 'Asset Code must be unique!')] # Building / Floor / Room @api.multi @api.constrains('building_id', 'floor_id', 'room_id') def _check_building(self): for rec in self: self.env['res.building']._check_room_location(rec.building_id, rec.floor_id, rec.room_id) @api.multi def _compute_manual(self): for rec in self: rec.manual = rec.depreciation_line_ids.mapped('manual') and \ True or False @api.onchange('building_id') def _onchange_building_id(self): self.floor_id = False self.room_id = False @api.onchange('floor_id') def _onchange_floor_id(self): self.room_id = False @api.multi def _compute_total_child_value(self): for rec in self: rec.total_child_value = sum(rec.child_ids.mapped('purchase_value')) return True @api.multi @api.depends('installment') def _compute_installment_str(self): for rec in self: if rec.installment: rec.installment_str = '%s/%s' % (rec.installment, rec.num_installment) return True @api.multi def validate_asset_to_request(self): invalid_assets = len(self.filtered(lambda l: l.doc_request_id or l.type != 'normal' or l.state != 'open')) if invalid_assets > 0: raise ValidationError( _('Please select only running assets ' 'that has not been requested yet!')) return True @api.multi def validate_asset_to_removal(self): invalid_assets = len(self.filtered(lambda l: l.type != 'normal' or l.state != 'open')) if invalid_assets > 0: raise ValidationError( _('Please select only running assets!')) return True @api.multi def action_undeliver_assets(self): """ This function is used for parent asset only """ status_normal = self.env.ref('pabi_asset_management.' 'asset_status_normal') for rec in self: rec.child_ids.write({'status': status_normal.id, 'deliver_to': False, 'deliver_date': False}) rec.state = 'draft' @api.multi def write(self, vals): Status = self.env['account.asset.status'] # Status follow state if 'state' in vals and vals.get('state', False): if vals.get('state') == 'close': vals['status'] = Status.search([('code', '=', 'expire')]).id if vals.get('state') == 'open': vals['status'] = Status.search([('code', '=', 'normal')]).id if vals.get('state') == 'draft': vals['status'] = Status.search([('code', '=', 'cancel')]).id # For removed, the state will be set in remove wizard # Validate status change must be within status map elif 'status' in vals and vals.get('status', False): status = Status.browse(vals.get('status')) for asset in self: if status.map_state != asset.state: raise ValidationError(_('Invalid change of asset status')) res = super(AccountAsset, self).write(vals) # # Following code repeat the compute depre, but w/o it, value is zero # for asset in self: # if asset.profile_id.open_asset and \ # self._context.get('create_asset_from_move_line'): # asset.compute_depreciation_board() # # -- return res @api.multi def open_source_asset(self): self.ensure_one() action = self.env.ref('account_asset_management.account_asset_action') result = action.read()[0] assets = self.with_context(active_test=False).\ search([('id', 'in', self.source_asset_ids.ids)]) dom = [('id', 'in', assets.ids)] result.update({'domain': dom, 'context': {'active_test': False}}) return result @api.multi def open_target_asset(self): self.ensure_one() action = self.env.ref('account_asset_management.account_asset_action') result = action.read()[0] assets = self.with_context(active_test=False).\ search([('id', 'in', self.target_asset_ids.ids)]) dom = [('id', 'in', assets.ids)] result.update({'domain': dom, 'context': {'active_test': False}}) return result @api.multi def open_depreciation_lines(self): self.ensure_one() action = self.env.ref('pabi_asset_management.' 'action_account_asset_line') result = action.read()[0] dom = [('asset_id', '=', self.id)] result.update({'domain': dom}) return result @api.multi def open_child_asset(self): self.ensure_one() action = self.env.ref('account_asset_management.account_asset_action') result = action.read()[0] assets = self.with_context(active_test=False).\ search([('id', 'in', self.child_ids.ids)]) dom = [('id', 'in', assets.ids)] result.update({'domain': dom, 'context': {'active_test': False}}) return result @api.multi def _compute_asset_count(self): # self = self.with_context(active_test=False) for asset in self: # _ids = self.with_context(active_test=False).\ # search([('target_asset_ids', 'in', [asset.id])])._ids asset.source_asset_count = \ len(asset.with_context(active_test=False).source_asset_ids) asset.target_asset_count = \ len(asset.with_context(active_test=False).target_asset_ids) asset.child_asset_count = \ len(asset.with_context(active_test=False).child_ids) @api.model def create(self, vals): # Case Parent Assets, AIT, AUC, ATM type = vals.get('type', False) ptype = vals.get('parent_type', False) if ptype and type == 'view': sequence_code = 'parent.asset.%s' % (ptype) vals['code'] = self.env['ir.sequence'].next_by_code(sequence_code) # Normal Case product_id = vals.get('product_id', False) if product_id: product = self.env['product.product'].browse(product_id) sequence = product.sequence_id if not sequence: raise ValidationError( _('No asset sequence setup for selected product!')) vals['code'] = self.env['ir.sequence'].next_by_id(sequence.id) # Set Salvage Value from Category profile_id = vals.get('profile_id', False) if profile_id: profile = self.env['account.asset.profile'].browse(profile_id) if profile and not profile.no_depreciation: vals['salvage_value'] = profile.salvage_value # -- asset = super(AccountAsset, self).create(vals) # Moved to before create # if asset.profile_id and not asset.profile_id.no_depreciation: # # This will also trigger new calc of depre base # asset._write({'salvage_value': asset.profile_id.salvage_value}) asset.update_related_dimension(vals) return asset @api.multi def name_get(self): res = [] for record in self: if record.code and record.code != '/': name = "[%s] %s" % (record.code, record.name) else: name = record.name res.append((record.id, name)) return res @api.multi def compute_depreciation_board(self): assets = self.filtered(lambda l: not l.no_depreciation) return super(AccountAsset, assets).compute_depreciation_board() # @api.multi # def onchange_profile_id(self, profile_id): # res = super(AccountAsset, self).onchange_profile_id(profile_id) # asset_profile = self.env['account.asset.profile'].browse(profile_id) # if asset_profile and not asset_profile.no_depreciation: # res['value']['salvage_value'] = asset_profile.salvage_value # return res @api.onchange('profile_id') def _onchange_profile_id(self): super(AccountAsset, self)._onchange_profile_id() if self.profile_id and not self.profile_id.no_depreciation: self.salvage_value = self.profile_id.salvage_value # Method used in change owner and transfer @api.model def _prepare_asset_reverse_moves(self, assets): AccountMoveLine = self.env['account.move.line'] default = {'move_id': False, 'parent_asset_id': False, 'asset_profile_id': False, 'product_id': False, 'partner_id': False, 'stock_move_id': False, } asset_move_lines_dict = [] depre_move_lines_dict = [] for asset in assets: account_asset_id = asset.profile_id.account_asset_id.id account_depre_id = asset.profile_id.account_depreciation_id.id # Getting the origin move_line (1 asset value and 1 depreciation) # Asset asset_lines = AccountMoveLine.search([ # Should have 1 line ('asset_id', '=', asset.id), ('account_id', '=', account_asset_id), # Same Owner ('project_id', '=', asset.owner_project_id.id), ('section_id', '=', asset.owner_section_id.id), ], order='id asc') if asset_lines: asset_line_dict = asset_lines[0].copy_data(default)[0] debit = sum(asset_lines.mapped('debit')) credit = sum(asset_lines.mapped('credit')) if debit > credit: asset_line_dict['credit'] = debit - credit asset_line_dict['debit'] = False else: asset_line_dict['credit'] = False asset_line_dict['debit'] = credit - debit asset_move_lines_dict.append(asset_line_dict) # Depre depre_lines = AccountMoveLine.search([ ('asset_id', '=', asset.id), ('account_id', '=', account_depre_id), # Same Owner ('project_id', '=', asset.owner_project_id.id), ('section_id', '=', asset.owner_section_id.id), ], order='id asc') if depre_lines: depre_line_dict = depre_lines[0].copy_data(default)[0] debit = sum(depre_lines.mapped('debit')) credit = sum(depre_lines.mapped('credit')) if debit > credit: asset_line_dict['credit'] = debit - credit asset_line_dict['debit'] = False else: asset_line_dict['credit'] = False asset_line_dict['debit'] = credit - debit depre_move_lines_dict.append(depre_line_dict) # Validation # if not asset_move_lines_dict: # raise ValidationError( # _('No Asset Value. Something went wrong!\nIt is likely ' # 'that, the asset owner do not match with account move.')) return (asset_move_lines_dict, depre_move_lines_dict) @api.model def _prepare_asset_target_move(self, move_lines_dict, new_owner=None): if new_owner is None: new_owner = {} debit = sum(x['debit'] for x in move_lines_dict) credit = sum(x['credit'] for x in move_lines_dict) if not move_lines_dict: raise ValidationError( _('Error on function _prepare_asset_target_move.\n' 'Invalid or no journal entry in original asset.')) move_line_dict = move_lines_dict[0].copy() move_line_dict.update({ 'analytic_account_id': False, # To refresh dimension 'credit': debit, 'debit': credit, }) if new_owner: move_line_dict.update({ 'project_id': new_owner.get('owner_project_id', False), 'section_id': new_owner.get('owner_section_id', False), }) return move_line_dict @api.multi def post_import_validation(self): for rec in self: # Delete some unused imported line rec.depreciation_line_ids.filtered( lambda x: # 1. copied posted line, 2 copied init line (not x.move_id and not x.manual) or (x.type != "create" and not x.line_days) ).unlink() # Re-assign previous id all over again to ensure # computed fields are valid previous_line = False for line in rec.depreciation_line_ids.\ filtered(lambda x: x.type == 'depreciate').\ sorted(key=lambda r: r.line_date): line.previous_id = previous_line previous_line = line
class res_partner(models.Model): _inherit = 'res.partner' @api.model def name_search(self, name, args=None, operator='ilike', limit=100): """ name_search(name='', args=None, operator='ilike', limit=100) -> records Search for records that have a display name matching the given ``name`` pattern when compared with the given ``operator``, while also matching the optional search domain (``args``). This is used for example to provide suggestions based on a partial value for a relational field. Sometimes be seen as the inverse function of :meth:`~.name_get`, but it is not guaranteed to be. This method is equivalent to calling :meth:`~.search` with a search domain based on ``display_name`` and then :meth:`~.name_get` on the result of the search. Extends the original method!!! If eq_filter_prod_sup in the context is true, then only the suppliers which sell all of the products from the purchase order lines (eq_order_line in context) are returned. :param str name: the name pattern to match :param list args: optional search domain (see :meth:`~.search` for syntax), specifying further restrictions :param str operator: domain operator for matching ``name``, such as ``'like'`` or ``'='``. :param int limit: optional max number of records to return :rtype: list :return: list of pairs ``(id, text_repr)`` for all matching records. """ if self._context.get('eq_filter_prod_sup') and self._context.get('eq_order_line'): product_ids = [] purchase_line_obj = self.env['purchase.order.line'] for line_data in self._context.get('eq_order_line'): if line_data[0] == 6: product_ids.append(line_data[2]['product_id']) elif line_data[0] == 4: purchase_order_line = purchase_line_obj.browse(line_data[1]) product_ids.append(purchase_order_line.product_id.id) elif line_data[0] == 1: if 'product_id' in line_data[2]: product_ids.append(line_data[2]['product_id']) else: purchase_order_line = purchase_line_obj.browse(line_data[1]) product_ids.append(purchase_order_line.product_id.id) if product_ids: sql_query = """select product_tmpl_id, name from product_supplierinfo where product_tmpl_id in %s""" % (str(tuple(product_ids))) self._cr.execute(sql_query) supplierinfo = self._cr.fetchall() prod_sup_mapping = {} for product_id, sup_id in supplierinfo: if sup_id in prod_sup_mapping: prod_sup_mapping[sup_id].append(product_id) else: prod_sup_mapping[sup_id] = [product_id] suppliers = [] for supplier_id, prod_list in prod_sup_mapping.iteritems(): if set(product_ids) <= set(prod_list): suppliers.append(supplier_id) if args == None: args = [] args.append(['id', 'in', suppliers]) res = super(res_partner, self).name_search(name, args=args, operator=operator, limit=limit) return res eq_delivery_date_type_purchase = fields.Selection([('cw', 'Calendar week'), ('date', 'Date')], string="Delivery Date Purchase", help="If nothing is selected, the default from the settings will be used.") eq_delivery_date_type_sale = fields.Selection([('cw', 'Calendar week'), ('date', 'Date')], string="Delivery Date Sale", help="If nothing is selected, the default from the settings will be used.") eq_complete_description = fields.Char(compute='_generate_complete_description', store=True) eq_prospective_customer = fields.Boolean(string="Prospective user",required=False, default=False) eq_unlocked_for_webshop = fields.Boolean(string="Unlocked for webshop",required=False, default=False) @api.one @api.depends('name', 'eq_firstname') def _generate_complete_description(self): for record in self: if record.is_company is False: result = "" if record.name is not False: result = record.name if record.eq_firstname is not False: if len(result) > 0: result += ", " + record.eq_firstname else: result = record.eq_firstname record.eq_complete_description = result else: record.eq_complete_description = record.name
class AccountInvoice(models.Model): _inherit = "account.invoice" main_id_number = fields.Char( related='commercial_partner_id.main_id_number', readonly=True, ) state_id = fields.Many2one( related='commercial_partner_id.state_id', store=True, readonly=True, ) currency_rate = fields.Float( string='Currency Rate', copy=False, digits=(16, 4), # TODO make it editable, we have to change move create method readonly=True, ) document_letter_id = fields.Many2one( related='document_type_id.document_letter_id', readonly=True, ) document_letter_name = fields.Char( related='document_letter_id.name', readonly=True, ) taxes_included = fields.Boolean( related='document_letter_id.taxes_included', readonly=True, ) afip_responsability_type_id = fields.Many2one( 'afip.responsability.type', string='AFIP Responsability Type', readonly=True, copy=False, ) invoice_number = fields.Integer( compute='_get_invoice_number', string="Invoice Number", ) point_of_sale_number = fields.Integer( compute='_get_invoice_number', string="Point Of Sale", ) # impuestos e importes de impuestos # todos los impuestos tipo iva (es un concepto mas bien interno) vat_tax_ids = fields.One2many( compute="_get_argentina_amounts", comodel_name='account.invoice.tax', string='VAT Taxes' ) # todos los impuestos iva que componene base imponible (no se incluyen 0, # 1, 2 que no son impuesto en si) vat_taxable_ids = fields.One2many( compute="_get_argentina_amounts", comodel_name='account.invoice.tax', string='VAT Taxes' ) # todos los impuestos menos los tipo iva vat_tax_ids not_vat_tax_ids = fields.One2many( compute="_get_argentina_amounts", comodel_name='account.invoice.tax', string='Not VAT Taxes' ) # suma de base para todos los impuestos tipo iva vat_base_amount = fields.Monetary( compute="_get_argentina_amounts", string='VAT Base Amount' ) # base imponible (no se incluyen 0, exento y no gravado) vat_taxable_amount = fields.Monetary( compute="_get_argentina_amounts", string='VAT Taxable Amount' ) # base iva exento vat_exempt_base_amount = fields.Monetary( compute="_get_argentina_amounts", string='VAT Exempt Base Amount' ) # base iva no gravado vat_untaxed_base_amount = fields.Monetary( compute="_get_argentina_amounts", string='VAT Untaxed Base Amount' ) # importe de iva vat_amount = fields.Monetary( compute="_get_argentina_amounts", string='VAT Amount' ) # importe de otros impuestos other_taxes_amount = fields.Monetary( compute="_get_argentina_amounts", string='Other Taxes Amount' ) afip_incoterm_id = fields.Many2one( 'afip.incoterm', 'Incoterm', readonly=True, states={'draft': [('readonly', False)]} ) point_of_sale_type = fields.Selection( related='journal_id.point_of_sale_type', readonly=True, ) # estos campos los agregamos en este modulo pero en realidad los usa FE # pero entendemos que podrian ser necesarios para otros tipos, por ahora # solo lo vamos a hacer requerido si el punto de venta es del tipo # electronico # TODO mejorar, este concepto deberia quedar fijo y no poder modificarse # una vez validada, cosa que pasaria por ej si cambias el producto afip_concept = fields.Selection( compute='_get_concept', # store=True, selection=[('1', 'Producto / Exportación definitiva de bienes'), ('2', 'Servicios'), ('3', 'Productos y Servicios'), ('4', '4-Otros (exportación)'), ], string="AFIP concept", ) afip_service_start = fields.Date( string='Service Start Date', readonly=True, states={'draft': [('readonly', False)]}, ) afip_service_end = fields.Date( string='Service End Date', readonly=True, states={'draft': [('readonly', False)]}, ) @api.one def _get_argentina_amounts(self): """ """ vat_taxes = self.tax_line_ids.filtered( lambda r: ( r.tax_id.tax_group_id.type == 'tax' and r.tax_id.tax_group_id.tax == 'vat')) # we add and "r.base" because only if a there is a base amount it is # considered taxable, this is used for eg to validate invoices on afip vat_taxables = vat_taxes.filtered( lambda r: ( r.tax_id.tax_group_id.afip_code not in [0, 1, 2]) and r.base) vat_amount = sum(vat_taxes.mapped('amount')) self.vat_tax_ids = vat_taxes self.vat_taxable_ids = vat_taxables self.vat_amount = vat_amount # self.vat_taxable_amount = sum(vat_taxables.mapped('base_amount')) self.vat_taxable_amount = sum(vat_taxables.mapped('base')) # self.vat_base_amount = sum(vat_taxes.mapped('base_amount')) self.vat_base_amount = sum(vat_taxes.mapped('base')) # vat exempt values # exempt taxes are the ones with code 2 vat_exempt_taxes = self.tax_line_ids.filtered( lambda r: ( r.tax_id.tax_group_id.type == 'tax' and r.tax_id.tax_group_id.tax == 'vat' and r.tax_id.tax_group_id.afip_code == 2)) self.vat_exempt_base_amount = sum( vat_exempt_taxes.mapped('base')) # self.vat_exempt_base_amount = sum( # vat_exempt_taxes.mapped('base_amount')) # vat_untaxed_base_amount values (no gravado) # vat exempt taxes are the ones with code 1 vat_untaxed_taxes = self.tax_line_ids.filtered( lambda r: ( r.tax_id.tax_group_id.type == 'tax' and r.tax_id.tax_group_id.tax == 'vat' and r.tax_id.tax_group_id.afip_code == 1)) self.vat_untaxed_base_amount = sum( vat_untaxed_taxes.mapped('base')) # self.vat_untaxed_base_amount = sum( # vat_untaxed_taxes.mapped('base_amount')) # other taxes values not_vat_taxes = self.tax_line_ids - vat_taxes other_taxes_amount = sum(not_vat_taxes.mapped('amount')) self.not_vat_tax_ids = not_vat_taxes self.other_taxes_amount = other_taxes_amount @api.multi @api.depends('document_number', 'number') def _get_invoice_number(self): """ Funcion que calcula numero de punto de venta y numero de factura a partir del document number. Es utilizado principalmente por el modulo de vat ledger citi """ # TODO mejorar estp y almacenar punto de venta y numero de factura por # separado, de hecho con esto hacer mas facil la carga de los # comprobantes de compra # decidimos obtener esto solamente para comprobantes con doc number for rec in self: str_number = rec.document_number or False if str_number: if rec.document_type_id.code in ['33', '99', '331', '332']: point_of_sale = '0' # leave only numbers and convert to integer invoice_number = str_number # despachos de importacion elif rec.document_type_id.code == '66': point_of_sale = '0' invoice_number = '0' elif "-" in str_number: splited_number = str_number.split('-') invoice_number = splited_number.pop() point_of_sale = splited_number.pop() elif "-" not in str_number and len(str_number) == 12: point_of_sale = str_number[:4] invoice_number = str_number[-8:] else: raise ValidationError(_( 'Could not get invoice number and point of sale for ' 'invoice id %i') % (rec.id)) rec.invoice_number = int( re.sub("[^0-9]", "", invoice_number)) rec.point_of_sale_number = int( re.sub("[^0-9]", "", point_of_sale)) @api.one @api.depends( 'invoice_line_ids', 'invoice_line_ids.product_id', 'invoice_line_ids.product_id.type', 'localization', ) def _get_concept(self): afip_concept = False if self.point_of_sale_type in ['online', 'electronic']: # exportaciones invoice_lines = self.invoice_line_ids product_types = set( [x.product_id.type for x in invoice_lines if x.product_id]) consumible = set(['consu', 'product']) service = set(['service']) mixed = set(['consu', 'service', 'product']) # default value "product" afip_concept = '1' if product_types.issubset(mixed): afip_concept = '3' if product_types.issubset(service): afip_concept = '2' if product_types.issubset(consumible): afip_concept = '1' if self.document_type_id.code in ['19', '20', '21']: # TODO verificar esto, como par expo no existe 3 y existe 4 # (otros), considermaos que un mixto seria el otros if afip_concept == '3': afip_concept = '4' self.afip_concept = afip_concept @api.multi def get_localization_invoice_vals(self): self.ensure_one() if self.localization == 'argentina': commercial_partner = self.partner_id.commercial_partner_id currency = self.currency_id.with_context( date=self.date_invoice or fields.Date.context_today(self)) if self.company_id.currency_id == currency: currency_rate = 1.0 else: currency_rate = currency.compute( 1., self.company_id.currency_id, round=False) return { 'afip_responsability_type_id': ( commercial_partner.afip_responsability_type_id.id), 'currency_rate': currency_rate, } else: return super( AccountInvoice, self).get_localization_invoice_vals() @api.model def _get_available_journal_document_types( self, journal, invoice_type, partner): """ This function search for available document types regarding: * Journal * Partner * Company * Documents configuration If needed, we can make this funcion inheritable and customizable per localization """ if journal.localization != 'argentina': return super( AccountInvoice, self)._get_available_journal_document_types( journal, invoice_type, partner) commercial_partner = partner.commercial_partner_id journal_document_types = journal_document_type = self.env[ 'account.journal.document.type'] if invoice_type in [ 'out_invoice', 'in_invoice', 'out_refund', 'in_refund']: if journal.use_documents: letters = journal.get_journal_letter( counterpart_partner=commercial_partner) domain = [ ('journal_id', '=', journal.id), '|', ('document_type_id.document_letter_id', 'in', letters.ids), ('document_type_id.document_letter_id', '=', False), ] # if invoice_type is refund, only credit notes if invoice_type in ['out_refund', 'in_refund']: domain += [ ('document_type_id.internal_type', # '=', 'credit_note')] # TODO, check if we need to add tickets and others # also 'in', ['credit_note', 'in_document'])] # else, none credit notes else: domain += [ ('document_type_id.internal_type', '!=', 'credit_note')] # If internal_type in context we try to serch specific document # for eg used on debit notes internal_type = self._context.get('internal_type', False) if internal_type: journal_document_type = journal_document_type.search( domain + [ ('document_type_id.internal_type', '=', internal_type)], limit=1) # For domain, we search all documents journal_document_types = journal_document_types.search(domain) # If not specific document type found, we choose another one if not journal_document_type and journal_document_types: journal_document_type = journal_document_types[0] if invoice_type == 'in_invoice': other_document_types = (commercial_partner.other_document_type_ids) domain = [ ('journal_id', '=', journal.id), ('document_type_id', 'in', other_document_types.ids), ] other_journal_document_types = self.env[ 'account.journal.document.type'].search(domain) journal_document_types += other_journal_document_types # if we have some document sepecific for the partner, we choose it if other_journal_document_types: journal_document_type = other_journal_document_types[0] return { 'available_journal_document_types': journal_document_types, 'journal_document_type': journal_document_type, } @api.multi @api.constrains('document_number', 'partner_id', 'company_id') def _check_document_number_unique(self): for rec in self.filtered(lambda x: x.localization == 'argentina'): if rec.document_number: domain = [ ('type', '=', rec.type), ('document_number', '=', rec.document_number), ('document_type_id', '=', rec.document_type_id.id), ('company_id', '=', rec.company_id.id), ('id', '!=', rec.id) ] msg = ( 'Error en factura con id %s: El numero de comprobante (%s)' ' debe ser unico por tipo de documento') if rec.type in ['out_invoice', 'out_refund']: # si es factura de cliente entonces tiene que ser numero # unico por compania y tipo de documento rec.search(domain) else: # si es factura de proveedor debe ser unica por proveedor domain += [ ('partner_id.commercial_partner_id', '=', rec.commercial_partner_id.id)] msg += ' y proveedor' if rec.search(domain): raise ValidationError(msg % (rec.id, rec.document_number)) @api.multi def action_move_create(self): """ We add currency rate on move creation so it can be used by electronic invoice later on action_number """ self.check_argentinian_invoice_taxes() return super(AccountInvoice, self).action_move_create() @api.multi def check_argentinian_invoice_taxes(self): """ We make theis function to be used as a constraint but also to be called from other models like vat citi """ # only check for argentinian localization companies _logger.info('Running checks related to argentinian documents') # we consider argentinian invoices the ones from companies with # localization localization and that belongs to a journal with # use_documents argentinian_invoices = self.filtered( lambda r: ( r.localization == 'argentina' and r.use_documents)) if not argentinian_invoices: return True # check partner has responsability so it will be assigned on invoice # validate without_responsability = argentinian_invoices.filtered( lambda x: not x.commercial_partner_id.afip_responsability_type_id) if without_responsability: raise ValidationError(_( 'The following invoices has a partner without AFIP ' 'responsability: %s' % without_responsability.ids)) # we check all invoice tax lines has tax_id related # we exclude exempt vats and untaxed (no gravados) wihtout_tax_id = argentinian_invoices.mapped('tax_line_ids').filtered( lambda r: not r.tax_id) if wihtout_tax_id: raise ValidationError(_( "Some Invoice Tax Lines don't have a tax_id asociated, please " "correct them or try to refresh invoice ")) # check codes has argentinian tax attributes configured tax_groups = argentinian_invoices.mapped( 'tax_line_ids.tax_id.tax_group_id') unconfigured_tax_groups = tax_groups.filtered( lambda r: not r.type or not r.tax or not r.application) if unconfigured_tax_groups: raise ValidationError(_( "You are using argentinian localization and there are some tax" " groups that are not configured. Tax Groups (id): %s" % ( ', '.join(unconfigured_tax_groups.mapped( lambda x: '%s (%s)' % (x.name, x.id)))))) vat_taxes = self.env['account.tax'].search([ ('tax_group_id.type', '=', 'tax'), ('tax_group_id.tax', '=', 'vat')]) lines_without_vat = self.env['account.invoice.line'].search([ ('invoice_id', 'in', argentinian_invoices.ids), ('invoice_line_tax_ids', 'not in', vat_taxes.ids)]) if lines_without_vat: raise ValidationError(_( "Invoice with ID %s has some lines without vat Tax ") % ( lines_without_vat.mapped('invoice_id').ids)) # for invoice in argentinian_invoices: # # TODO usar round # # TODO tal vez debamos usar esto para un chequeo de suma de # # importes y demas, tener en cuenta caso de importaciones # # tal como esta este chequeo da error si se agregan impuestos # # manuales # if abs(invoice.vat_base_amount - invoice.amount_untaxed) > 0.1: # raise ValidationError(_( # "Invoice with ID %i has some lines without vat Tax ") % ( # invoice.id)) # Check except vat invoice afip_exempt_codes = ['Z', 'X', 'E', 'N', 'C'] for invoice in argentinian_invoices: special_vat_taxes = invoice.tax_line_ids.filtered( lambda r: r.tax_id.tax_group_id.afip_code in [1, 2, 3]) if ( special_vat_taxes and invoice.fiscal_position_id.afip_code not in afip_exempt_codes): raise ValidationError(_( "If you have choose a 0, exempt or untaxed 'tax', " "you must choose a fiscal position with afip code in %s.\n" "* Invoice id %i" % (afip_exempt_codes, invoice.id)) ) # TODO sacamos esto porque no era muy lindo y daba algunos errores con # el account_fix, hicimos que los datos demo hagan el compute tax # habria que ver una mejor forma de hacerlo para que tambien ande bien si # se importa desde interfaz # If we import or get demo data # tax_id is not loaded on tax lines, we couldn't find the error # so we add this to fix it # @api.one # @api.constrains('invoice_line_ids') # def update_taxes_fix(self): # context = dict(self._context) # if context.get('constraint_update_taxes'): # return True # self.with_context(constraint_update_taxes=True).compute_taxes() # we add fiscal position with fp method instead of directly from partner # TODO. this should go in a PR to ODOO @api.onchange('partner_id', 'company_id') def _onchange_partner_id(self): res = super(AccountInvoice, self)._onchange_partner_id() fiscal_position = self.env[ 'account.fiscal.position'].with_context( force_company=self.company_id.id).get_fiscal_position( self.partner_id.id) if fiscal_position: self.fiscal_position_id = fiscal_position return res @api.one @api.constrains('date_invoice') def set_date_afip(self): if self.date_invoice: date_invoice = fields.Datetime.from_string(self.date_invoice) if not self.afip_service_start: self.afip_service_start = date_invoice + relativedelta(day=1) if not self.afip_service_end: self.afip_service_end = date_invoice + \ relativedelta(day=1, days=-1, months=+1)
class AtQuestion(models.Model): """ Questions are the academy tests cornerstone. Each one of the questions belongs to a single topic but they can belong to more than one question in the selected topic. Fields: name (Char): Human readable name which will identify each record. """ _name = 'at.question' _description = u'Referred question' _rec_name = 'name' _order = 'name ASC' _inherit = ['mail.thread'] # ---------------------------- ENTITY FIELDS ------------------------------ name = fields.Char(string='Name', required=True, readonly=False, index=True, default=None, help='Text for this question', size=250, translate=True, track_visibility='onchange') description = fields.Text(string='Description', required=False, readonly=False, index=False, default=None, help='Something about this question', translate=True) active = fields.Boolean( string='Active', required=False, readonly=False, index=False, default=True, help=('If the active field is set to false, it will allow you to ' 'hide record without removing it.')) at_topic_id = fields.Many2one(string='Topic', required=False, readonly=False, index=False, default=None, help='Topic to which this question belongs', comodel_name='at.topic', domain=[], context={}, ondelete='cascade', auto_join=False, track_visibility='onchange') at_category_ids = fields.Many2many( string='Categories', required=True, readonly=False, index=False, default=None, help='Categories relating to this question', comodel_name='at.category', #relation='model_name_this_model_rel', #column1='model_name_id}', #column2='this_model_id', domain=lambda self: self._compute_at_category_ids_domain(), context={}, limit=None, track_visibility='onchange') at_answer_ids = fields.One2many( string='Answers', required=True, readonly=False, index=False, default=None, help='Answers will be shown as choice options for this question', comodel_name='at.answer', inverse_name='at_question_id', domain=[], context={}, auto_join=False, limit=None, track_visibility='onchange') at_tag_ids = fields.Many2many( string='Tags', required=False, readonly=False, index=False, default=None, help='Tag can be used to better describe this question', comodel_name='at.tag', # relation='model_name_this_model_rel', # column1='model_name_id}', # column2='this_model_id', domain=[], context={}, limit=None, track_visibility='onchange') at_level_id = fields.Many2one( string='Difficulty level', required=True, readonly=False, index=False, default=lambda self: self._default_at_level_id(), help='Difficulty level of this question', comodel_name='at.level', domain=[], context={}, ondelete='cascade', auto_join=False, track_visibility='onchange') ir_attachment_ids = fields.Many2many( string='Attachments', required=False, readonly=False, index=False, default=None, help='Attachments needed to solve this question', comodel_name='ir.attachment', relation='ir_attachment_this_model_rel', column1='ir_attachment_id', column2='this_model_id', domain=[], context={}, limit=None) # ----------------------- AUXILIARY FIELD METHODS ------------------------- def _compute_at_category_ids_domain(self): """ Computes domain for at_category_ids, this should allow categories only in the selected topic. """ id_list = self.at_topic_id.at_category_ids.mapped('id') return [('id', 'in', tuple(id_list))] def _default_at_level_id(self): """ Computes the level_id default value """ at_level_domain = [] at_level_obj = self.env['at.level'] at_level_set = at_level_obj.search(at_level_domain, order="sequence ASC", limit=1) return at_level_set[0].id if at_level_set else None # --------------------------- ONCHANGE EVENTS ----------------------------- @api.onchange('at_topic_id') def _onchange_at_topid_id(self): """ Updates domain form at_category_ids, this shoud allow categories only in the selected topic. """ domain = self._compute_at_category_ids_domain() _logger.debug(domain) return {'domain': {'at_category_ids': domain}} # -------------------------- PYTHON CONSTRAINS ---------------------------- @api.one @api.constrains('at_answer_ids') def _check_at_answer_ids(self): """ Check if question have at last one valid answer """ message = _(u'You must specify at least one correct answer') if not True in self.at_answer_ids.mapped('is_correct'): raise ValidationError(message) # --------------------------- SQL_CONTRAINTS ------------------------------ _sql_constraints = [ ('question_uniq', 'UNIQUE(name)', _(u'There is already another question with the same name')) ]
class AccountFiscalPositionTemplate(models.Model): _inherit = 'account.fiscal.position.template' name = fields.Char('Fiscal Position', size=128, required=True) fiscal_category_id = fields.Many2one('l10n_br_account.fiscal.category', 'Categoria Fiscal') fiscal_category_fiscal_type = fields.Selection( PRODUCT_FISCAL_TYPE, related='fiscal_category_id.fiscal_type', readonly=True, store=True, string='Fiscal Type') type = fields.Selection([('input', 'Entrada'), ('output', 'Saida')], 'Tipo') type_tax_use = fields.Selection([('sale', 'Sale'), ('purchase', 'Purchase'), ('all', 'All')], 'Tax Application') inv_copy_note = fields.Boolean('Copiar Observação na Nota Fiscal') asset_operation = fields.Boolean( 'Operação de Aquisição de Ativo', help="""Caso seja marcada essa opção, será incluido o IPI na base de calculo do ICMS.""") state = fields.Selection([('draft', u'Rascunho'), ('review', u'Revisão'), ('approved', u'Aprovada'), ('unapproved', u'Não Aprovada')], 'Status', readonly=True, track_visibility='onchange', select=True, default='draft') @api.multi def onchange_type(self, type): type_tax = {'input': 'purhcase', 'output': 'sale'} return { 'value': { 'type_tax_use': type_tax.get(type, 'all'), 'tax_ids': False } } @api.multi def onchange_fiscal_category_id(self, fiscal_category_id=None): result = {'value': {}} if fiscal_category_id: fiscal_category = self.env[ 'l10n_br_account.fiscal.category'].browse(fiscal_category_id) result['value'].update( {'fiscal_category_fiscal_type': fiscal_category.fiscal_type}) return result def generate_fiscal_position(self, cr, uid, chart_temp_id, tax_template_ref, acc_template_ref, company_id, context=None): """ This method generate Fiscal Position, Fiscal Position Accounts and Fiscal Position Taxes from templates. :param chart_temp_id: Chart Template Id. :param taxes_ids: Taxes templates reference for generating account.fiscal.position.tax. :param acc_template_ref: Account templates reference for generating account.fiscal.position.account. :param company_id: selected from wizard.multi.charts.accounts. :returns: True """ if context is None: context = {} obj_tax_fp = self.pool.get('account.fiscal.position.tax') obj_ac_fp = self.pool.get('account.fiscal.position.account') obj_fiscal_position = self.pool.get('account.fiscal.position') obj_tax_code = self.pool.get('account.tax.code') obj_tax_code_template = self.pool.get('account.tax.code.template') tax_code_template_ref = {} tax_code_ids = obj_tax_code.search(cr, uid, [('company_id', '=', company_id)]) for tax_code in obj_tax_code.browse(cr, uid, tax_code_ids): tax_code_template = obj_tax_code_template.search( cr, uid, [('name', '=', tax_code.name)]) if tax_code_template: tax_code_template_ref[tax_code_template[0]] = tax_code.id fp_ids = self.search(cr, uid, [('chart_template_id', '=', chart_temp_id)]) for position in self.browse(cr, uid, fp_ids, context=context): new_fp = obj_fiscal_position.create( cr, uid, { 'company_id': company_id, 'name': position.name, 'note': position.note, 'type': position.type, 'state': position.state, 'type_tax_use': position.type_tax_use, 'cfop_id': position.cfop_id and position.cfop_id.id or False, 'inv_copy_note': position.inv_copy_note, 'asset_operation': position.asset_operation, 'fiscal_category_id': position.fiscal_category_id and position.fiscal_category_id.id or False }) for tax in position.tax_ids: obj_tax_fp.create( cr, uid, { 'tax_src_id': tax.tax_src_id and tax_template_ref.get(tax.tax_src_id.id, False), 'tax_code_src_id': tax.tax_code_src_id and tax_code_template_ref.get( tax.tax_code_src_id.id, False), 'tax_src_domain': tax.tax_src_domain, 'tax_dest_id': tax.tax_dest_id and tax_template_ref.get(tax.tax_dest_id.id, False), 'tax_code_dest_id': tax.tax_code_dest_id and tax_code_template_ref.get( tax.tax_code_dest_id.id, False), 'position_id': new_fp }) for acc in position.account_ids: obj_ac_fp.create( cr, uid, { 'account_src_id': acc_template_ref[acc.account_src_id.id], 'account_dest_id': acc_template_ref[acc.account_dest_id.id], 'position_id': new_fp }) return True
class stock_print_stock_voucher(models.TransientModel): _name = 'stock.print_stock_voucher' _description = "Print Stock Voucher" @api.model def _get_picking(self): active_id = self._context.get('active_id', False) return self.env['stock.picking'].browse(active_id) @api.model def _get_book(self): picking = self._get_picking() return picking.picking_type_id.book_id picking_id = fields.Many2one( 'stock.picking', default=_get_picking, required=True, ) printed = fields.Boolean(compute='_get_printed', ) book_id = fields.Many2one( 'stock.book', 'Book', default=_get_book, ) next_voucher_number = fields.Integer( 'Next Voucher Number', related='book_id.sequence_id.number_next_actual', readonly=True, ) estimated_number_of_pages = fields.Integer('Number of Pages', ) lines_per_voucher = fields.Integer( 'Lines Per Voucher', related='book_id.lines_per_voucher', ) @api.depends('picking_id', 'picking_id.voucher_ids') def _get_printed(self): printed = False if self.picking_id.voucher_ids: printed = True self.printed = printed @api.onchange('book_id', 'picking_id') def get_estimated_number_of_pages(self): lines_per_voucher = self.lines_per_voucher if lines_per_voucher == 0: estimated_number_of_pages = 1 else: operations = len(self.picking_id.pack_operation_ids) estimated_number_of_pages = ceil( float(operations) / float(lines_per_voucher)) self.estimated_number_of_pages = estimated_number_of_pages @api.multi def do_print_voucher(self): return self.picking_id.do_print_voucher() @api.one def assign_numbers(self): self.picking_id.assign_numbers(self.estimated_number_of_pages, self.book_id) @api.multi def do_print_and_assign(self): self.assign_numbers() return self.do_print_voucher() @api.multi def do_clean(self): self.picking_id.voucher_ids.unlink() self.picking_id.book_id = False
class wizard_sales_details(models.TransientModel): _name = 'wizard.sales.details' _description = 'Used to Store Wizard Sales Details.' @api.model def get_ip(self): proxy_ip = self.env['res.users'].browse( [self._uid]).company_id.report_ip_address or '' return proxy_ip start_date = fields.Date(string="Start Date") end_date = fields.Date(string="End Date") report_type = fields.Selection([('thermal', 'Thermal'), ('pdf', 'PDF')], default='pdf', string="Report Type") user_ids = fields.Many2many('res.users', 'acespritech_pos_details_report_user_rel', 'user_id', 'wizard_id', 'Salespeople') proxy_ip = fields.Char(string="Proxy IP", default=get_ip) only_summary = fields.Boolean("Only Summary") @api.multi def print_sales_details(self): datas = { 'ids': self._ids, 'form': self.read()[0], 'model': 'wizard.sales.details' } return self.env.ref( 'flexiretail_ee_advance.report_sales_details_pdf').report_action( self, data=datas) @api.onchange('start_date', 'end_date') def onchange_date(self): if self.start_date and self.end_date and self.start_date > self.end_date: raise Warning(_('End date should be greater than start date.')) @api.multi def print_pos_sale_action(self): if self.start_date > self.end_date: raise Warning(_('End date should be greater than start date.')) return True @api.multi def get_currency_id(self): user_id = self.env['res.users'].browse([self._uid]) if user_id: currency_id = user_id.company_id.currency_id return currency_id else: return @api.multi def get_current_date(self): if self._context and self._context.get('tz'): tz_name = self._context['tz'] else: tz_name = self.env['res.users'].browse([self._uid]).tz if tz_name: tz = timezone(tz_name) c_time = datetime.now(tz) return c_time.strftime('%d/%m/%Y') else: return date.today().strftime('%d/%m/%Y') @api.multi def get_current_time(self): if self._context and self._context.get('tz'): tz_name = self._context['tz'] else: tz_name = self.env['res.users'].browse([self._uid]).tz if tz_name: tz = timezone(tz_name) c_time = datetime.now(tz) return c_time.strftime('%I:%M %p') else: return datetime.now().strftime('%I:%M:%S %p') @api.multi def get_all_users(self): user_obj = self.env['res.users'] return [user.id for user in user_obj.search([])] def start_end_date_global(self, start, end, tz): tz = timezone(tz) or 'UTC' current_time = datetime.now(tz) hour_tz = int(str(current_time)[-5:][:2]) min_tz = int(str(current_time)[-5:][3:]) sign = str(current_time)[-6][:1] sdate = str(start) + " 00:00:00" edate = str(end) + " 23:59:59" if sign == '-': start_date = (datetime.strptime(sdate, '%Y-%m-%d %H:%M:%S') + timedelta(hours=hour_tz, minutes=min_tz) ).strftime("%Y-%m-%d %H:%M:%S") end_date = (datetime.strptime(edate, '%Y-%m-%d %H:%M:%S') + timedelta(hours=hour_tz, minutes=min_tz) ).strftime("%Y-%m-%d %H:%M:%S") if sign == '+': start_date = (datetime.strptime(sdate, '%Y-%m-%d %H:%M:%S') - timedelta(hours=hour_tz, minutes=min_tz) ).strftime("%Y-%m-%d %H:%M:%S") end_date = (datetime.strptime(edate, '%Y-%m-%d %H:%M:%S') - timedelta(hours=hour_tz, minutes=min_tz) ).strftime("%Y-%m-%d %H:%M:%S") return start_date, end_date @api.multi def get_precision(self, price): precision = self.env['decimal.precision'].precision_get( 'Product Price') total_price_formatted = "{:.{}f}".format(price, precision) return total_price_formatted @api.multi def get_total_sales(self, user_lst=None): if self: total_sales = 0.0 pos_obj = self.env['pos.order'] user_obj = self.env['res.users'] if not user_lst: user_ids = [user.id for user in self.user_ids] or self.get_all_users() else: user_ids = user_lst company_id = user_obj.browse([self._uid]).company_id.id first_date, last_date = self.start_end_date_global( self.start_date, self.end_date, self.env['res.users'].browse([self._uid]).tz) pos_ids = pos_obj.search([('date_order', '>=', first_date), \ ('date_order', '<=', last_date), \ ('user_id', 'in', user_ids), ('state', 'in', ['done', 'paid', 'invoiced']), ('company_id', '=', company_id)]) if pos_ids: for pos in pos_ids: for pol in pos.lines: total_sales += (pol.price_unit * pol.qty) return total_sales @api.multi def get_total_returns(self, user_lst=None): if self: pos_order_obj = self.env['pos.order'] if not user_lst: user_ids = [user.id for user in self.user_ids] or self.get_all_users() else: user_ids = user_lst user_obj = self.env['res.users'] company_id = user_obj.browse([self._uid]).company_id.id total_return = 0.0 first_date, last_date = self.start_end_date_global( self.start_date, self.end_date, self.env['res.users'].browse([self._uid]).tz) for pos in pos_order_obj.search([('date_order', '>=', first_date), ('date_order', '<=', last_date), ('user_id', 'in', user_ids), ('state', 'in', ['done', 'paid', 'invoiced']), ('company_id', '=', company_id)]): total_return += pos.amount_total return total_return @api.multi def get_tax_amount(self, user_lst=None): if self: amount_tax = 0.0 if not user_lst: user_ids = [user.id for user in self.user_ids] or self.get_all_users() else: user_ids = user_lst pos_order_obj = self.env['pos.order'] first_date, last_date = self.start_end_date_global( self.start_date, self.end_date, self.env['res.users'].browse([self._uid]).tz) company_id = self.env['res.users'].browse([self._uid ]).company_id.id pos_ids = pos_order_obj.search([('date_order', '>=', first_date), ('date_order', '<=', last_date), ('state', 'in', ['paid', 'invoiced', 'done']), ('user_id', 'in', user_ids), ('company_id', '=', company_id)]) if pos_ids: for order in pos_ids: amount_tax += order.amount_tax return amount_tax @api.multi def get_total_discount(self, user_lst=None): if self: total_discount = 0.0 if not user_lst: user_ids = [user.id for user in self.user_ids] or self.get_all_users() else: user_ids = user_lst pos_order_obj = self.env['pos.order'] company_id = self.env['res.users'].browse([self._uid ]).company_id.id first_date, last_date = self.start_end_date_global( self.start_date, self.end_date, self.env['res.users'].browse([self._uid]).tz) pos_ids = pos_order_obj.search([('date_order', '>=', first_date), ('date_order', '<=', last_date), ('state', 'in', ['paid', 'invoiced', 'done']), ('user_id', 'in', user_ids), ('company_id', '=', company_id)]) if pos_ids: for order in pos_ids: # total_discount += sum([((line.qty * line.price_unit) * line.discount) / 100 for line in order.lines]) discount_product_id = False is_discount = order.session_id.config_id.module_pos_discount if is_discount: discount_product_id = order.session_id.config_id.discount_product_id.id for line in order.lines: total_discount += sum([ ((line.qty * line.price_unit) * line.discount) / 100 ]) if line.product_id.id == discount_product_id: total_discount += abs(line.price_subtotal_incl) return total_discount @api.multi def get_total_first(self, user_lst=None): user_lst = user_lst or [] if self: total = (self.get_total_sales(user_lst) + self.get_tax_amount(user_lst))\ - (abs(self.get_total_discount(user_lst))) return total @api.multi def get_user(self): if self._uid == SUPERUSER_ID: return True @api.multi def get_gross_total(self, user_lst=None): if self: gross_total = 0.0 if not user_lst: user_ids = [user.id for user in self.user_ids] or self.get_all_users() else: user_ids = user_lst pos_order_obj = self.env['pos.order'] first_date, last_date = self.start_end_date_global( self.start_date, self.end_date, self.env['res.users'].browse([self._uid]).tz) company_id = self.env['res.users'].browse([self._uid ]).company_id.id pos_ids = pos_order_obj.search([('date_order', '>=', first_date), ('date_order', '<=', last_date), ('state', 'in', ['paid', 'invoiced', 'done']), ('user_id', 'in', user_ids), ('company_id', '=', company_id)]) if pos_ids: for order in pos_ids: for line in order.lines: gross_total += line.qty * ( line.product_id.lst_price - line.product_id.standard_price) return gross_total @api.multi def get_net_gross_total(self, user_lst=None): user_lst = user_lst or [] if self: net_gross_profit = 0.0 net_gross_profit = self.get_gross_total( user_lst) - self.get_tax_amount(user_lst) return net_gross_profit @api.multi def get_product_category(self, user_lst=None): if self: product_list = [] if not user_lst: user_ids = [user.id for user in self.user_ids] or self.get_all_users() else: user_ids = user_lst pos_order_obj = self.env['pos.order'] company_id = self.env['res.users'].browse([self._uid ]).company_id.id first_date, last_date = self.start_end_date_global( self.start_date, self.end_date, self.env['res.users'].browse([self._uid]).tz) pos_ids = pos_order_obj.search([('date_order', '>=', first_date), ('date_order', '<=', last_date), ('state', 'in', ['paid', 'invoiced', 'done']), ('user_id', 'in', user_ids), ('company_id', '=', company_id)]) if pos_ids: for order in pos_ids: for line in order.lines: flag = False product_dict = {} for lst in product_list: if line.product_id.pos_categ_id: if lst.get( 'pos_categ_id' ) == line.product_id.pos_categ_id.id: lst['price'] = lst['price'] + ( line.qty * line.price_unit) flag = True else: if lst.get('pos_categ_id') == '': lst['price'] = lst['price'] + ( line.qty * line.price_unit) flag = True if not flag: product_dict.update({ 'pos_categ_id': line.product_id.pos_categ_id and line.product_id.pos_categ_id.id or '', 'price': (line.qty * line.price_unit) }) product_list.append(product_dict) return product_list @api.multi def get_product_name(self, category_id): if category_id: category_name = self.env['pos.category'].browse([category_id]).name return category_name @api.multi def get_product_cate_total(self, user_lst=None): if self: balance_end_real = 0.0 if not user_lst: user_ids = [user.id for user in self.user_ids] or self.get_all_users() else: user_ids = user_lst pos_order_obj = self.env['pos.order'] company_id = self.env['res.users'].browse([self._uid ]).company_id.id first_date, last_date = self.start_end_date_global( self.start_date, self.end_date, self.env['res.users'].browse([self._uid]).tz) pos_ids = pos_order_obj.search([('date_order', '>=', first_date), ('date_order', '<=', last_date), ('state', 'in', ['paid', 'invoiced', 'done']), ('user_id', 'in', user_ids), ('company_id', '=', company_id)]) if pos_ids: for order in pos_ids: for line in order.lines: balance_end_real += (line.qty * line.price_unit) return balance_end_real @api.multi def get_payments(self, user_lst=None): if self: statement_line_obj = self.env["account.bank.statement.line"] pos_order_obj = self.env["pos.order"] if not user_lst: user_ids = [user.id for user in self.user_ids] or self.get_all_users() else: user_ids = user_lst company_id = self.env['res.users'].browse([self._uid ]).company_id.id first_date, last_date = self.start_end_date_global( self.start_date, self.end_date, self.env['res.users'].browse([self._uid]).tz) pos_ids = pos_order_obj.search([('date_order', '>=', first_date), ('date_order', '<=', last_date), ('state', 'in', ['paid', 'invoiced', 'done']), ('user_id', 'in', user_ids), ('company_id', '=', company_id)]) data = {} if pos_ids: pos_ids = [pos.id for pos in pos_ids] st_line_ids = statement_line_obj.search([('pos_statement_id', 'in', pos_ids)]) if st_line_ids: a_l = [] for r in st_line_ids: a_l.append(r['id']) self._cr.execute("select aj.name,sum(amount) from account_bank_statement_line as absl,account_bank_statement as abs,account_journal as aj " \ "where absl.statement_id = abs.id and abs.journal_id = aj.id and absl.id IN %s " \ "group by aj.name ",(tuple(a_l),)) data = self._cr.dictfetchall() return data else: return {} @api.multi def get_user_wise_data(self): user_ids = self.user_ids or self.env['res.users'].search([]) result = {} for user in user_ids: result.update({ user.name: { 'total_discount': self.get_total_discount([user.id]), 'total_sales': self.get_total_sales([user.id]), 'total': self.get_total_returns([user.id]), 'taxes': self.get_tax_amount([user.id]), 'gross_total': self.get_total_first([user.id]), 'gross_profit': self.get_gross_total([user.id]), 'net_gross': self.get_net_gross_total([user.id]), 'payment': self.get_payments([user.id]), 'product_category': self.get_product_category([user.id]), 'prod_categ_total': self.get_product_cate_total([user.id]), } }) return result
class qdodoo_car_sale_contract(models.Model): """ 销售合同 """ _name = 'qdodoo.car.sale.contract' _rec_name = 'contract_num' _order = 'id desc' name = fields.Char(u'合同编号') contract_num = fields.Char( u'合同号', required=True, default=lambda self: str(datetime.now().year) + '-SDHSLGXHSC-') partner_id = fields.Many2one('res.partner', u'客户', required=True) date_order = fields.Date(u'合同日期', required=True, default=datetime.now().date()) car_number = fields.Float(u'车辆数', compute="_get_car_number") amount_total = fields.Float(u'金额', compute="_get_car_number") deposit_rate = fields.Float(u'保证金比例(%)', required=True) state = fields.Selection([('draft', u'草稿'), ('doing', u'执行'), ('done', u'完成'), ('cancel', u'取消'), ('no_normal', u'异常')], u'状态', default='draft') order_line = fields.One2many('qdodoo.car.sale.contract.line', 'order_id', u'车辆明细') file_name = fields.Char(u'合同名称', copy=False) contract_file = fields.Binary(u'合同扫描件', copy=False) pledge_money = fields.Float(u'保证金已收金额', copy=False) currency_id = fields.Many2one('res.currency', u'外币币种', required=True) currency_raise = fields.Float(u'外币汇率', required=True, digits=(14, 6)) is_issuing = fields.Boolean(u'已收保证金', copy=False) is_make_invoice = fields.Boolean(u'全部已开票', copy=False) is_payment = fields.Boolean(u'全部已收提车款', copy=False) is_settlement = fields.Boolean(u'已结算', copy=False) dalay_date = fields.Float(u'延期天数', default='90') dalay_rate = fields.Float(u'押汇利率(%)', default='4.5') dalay_china_rate = fields.Float(u'延期利率(%)人民币', default='10') _sql_constraints = [ ('contract_num_uniq', 'unique(contract_num)', '销售合同号已存在!'), ] @api.onchange('currency_id') def onchange_currcency(self): if self.currency_id: self.currency_raise = 1 / self.currency_id.rate_silent # 只能删除草稿或取消的订单 @api.multi def unlink(self): for ids in self: if ids.state not in ('draft', 'cancel'): raise osv.except_osv(_(u'错误'), _(u'只能删除草稿或取消的订单!')) return super(qdodoo_car_sale_contract, self).unlink() # 获取唯一的编号 @api.model def create(self, vals): if not vals.get('name'): vals['name'] = self.env['ir.sequence'].get( 'qdodoo.car.sale.contract') return super(qdodoo_car_sale_contract, self).create(vals) # 获取车辆数量、金额 def _get_car_number(self): for ids in self: number = 0 money = 0 for line in ids.order_line: number += line.product_qty money += line.all_money ids.car_number = number ids.amount_total = money # 押汇申请(销售) @api.multi def btn_negotiation_sale(self): negotiation_obj = self.env['qdodoo.car.negotiation.manager.sale'] # 判断是否存在押汇 negotiation_id = negotiation_obj.search([('purchase_id.contract_id', '=', self.id), ('state', '!=', 'cancel')]) result = self.env['ir.model.data'].get_object_reference( 'qdodoo_car_import_trade', 'tree_qdodoo_car_negotiation_manager_sale') view_id = result and result[1] or False result_form = self.env['ir.model.data'].get_object_reference( 'qdodoo_car_import_trade', 'form_qdodoo_car_negotiation_manager_sale') view_id_form = result_form and result_form[1] or False return { 'name': _('押汇'), 'view_type': 'form', "view_mode": 'tree,form', 'res_model': 'qdodoo.car.negotiation.manager.sale', 'type': 'ir.actions.act_window', 'domain': [('id', '=', negotiation_id.ids)], 'views': [(view_id, 'tree'), (view_id_form, 'form')], 'view_id': [view_id], } @api.one # 确认销售合同(生成车辆档案) def btn_doing(self): information_obj = self.env['qdodoo.car.information'] for ids in self: if not ids.order_line: raise osv.except_osv(_('错误!'), _('请输入车辆明细.')) for line in ids.order_line: # 判断如果存在车架号,则数量只能为1 if line.product_num and line.product_qty != 1: raise osv.except_osv(_('错误!'), _('填写车架号的明细数量只能为1.')) line.write({'rest_qty': line.product_qty}) val = {} val['sale_contract'] = ids.id val['product_id'] = line.product_id.id val['price_unit'] = line.price_unit val['tax_money'] = line.tax_money val['product_num'] = line.product_num val['pledge_money'] = line.pledge_money / line.product_qty val['agent_money'] = line.agent_money val['currency_id'] = self.currency_id.id if line.product_qty > 1: for k in range(line.product_qty): information_obj.create(val) else: information_obj.create(val) return self.write({'state': 'doing'}) @api.multi # 保证金(收款通知) def btn_predict_money(self): payment_obj = self.env['qdodoo.car.payment.order'] line_obj = self.env['qdodoo.car.payment.line'] information_obj = self.env['qdodoo.car.information'] domain = [('type', '=', 'collection'), ('contract_id', '=', self.id), ('is_pledge', '=', True), ('state', '!=', 'cancel')] value = { 'type': 'collection', 'contract_id': self.id, 'partner_id': self.partner_id.id, 'is_pledge': True } # 是保证金 model_id, pay_project = self.env['ir.model.data'].get_object_reference( 'qdodoo_car_import_trade', 'qdodoo_car_expense_1') value['pay_project'] = pay_project # 判断是否存在收款通知 payment_id = payment_obj.search(domain) if not payment_id: # 创建对应的收款通知 payment_id = payment_obj.create(value) # 创建对应的收款明细 # 获取销售订单对应的车辆档案 information_ids = information_obj.search([('sale_contract', '=', self.id)]) for information_id in information_ids: money = information_id.pledge_money line_obj.create({ 'order_id': payment_id.id, 'product_num': information_id.id, 'money': money }) result = self.env['ir.model.data'].get_object_reference( 'qdodoo_car_import_trade', 'tree_qdodoo_car_payment_order') view_id = result and result[1] or False result_form = self.env['ir.model.data'].get_object_reference( 'qdodoo_car_import_trade', 'form_qdodoo_car_payment_order') view_id_form = result_form and result_form[1] or False return { 'name': _('收款通知'), 'view_type': 'form', "view_mode": 'tree,form', 'res_model': 'qdodoo.car.payment.order', 'type': 'ir.actions.act_window', 'domain': [('id', '=', payment_id.id)], 'views': [(view_id, 'tree'), (view_id_form, 'form')], 'view_id': [view_id], } @api.multi # 二次保证金 def btn_predict_money_two(self): margin_obj = self.env['qdodoo.car.margin.money'] margin_ids = margin_obj.search([('partner_id', '=', self.partner_id.id) ]) result = self.env['ir.model.data'].get_object_reference( 'qdodoo_car_import_trade', 'tree_qdodoo_car_margin_money') view_id = result and result[1] or False result_form = self.env['ir.model.data'].get_object_reference( 'qdodoo_car_import_trade', 'form_qdodoo_car_margin_money') view_id_form = result_form and result_form[1] or False return { 'name': _('二次保证金'), 'view_type': 'form', "view_mode": 'tree,form', 'res_model': 'qdodoo.car.margin.money', 'type': 'ir.actions.act_window', 'domain': [('id', '=', margin_ids.ids)], 'context': { 'partner_id': self.partner_id.id }, 'views': [(view_id, 'tree'), (view_id_form, 'form')], 'view_id': [view_id], } @api.multi # 提车款 def btn_bring_car(self): carry_obj = self.env['qdodoo.car.carry.money'] carry_ids = carry_obj.search([('partner_id', '=', self.partner_id.id)]) result = self.env['ir.model.data'].get_object_reference( 'qdodoo_car_import_trade', 'tree_qdodoo_car_carry_money') view_id = result and result[1] or False result_form = self.env['ir.model.data'].get_object_reference( 'qdodoo_car_import_trade', 'form_qdodoo_car_carry_money') view_id_form = result_form and result_form[1] or False return { 'name': _('提车款'), 'view_type': 'form', "view_mode": 'tree,form', 'res_model': 'qdodoo.car.carry.money', 'type': 'ir.actions.act_window', 'domain': [('id', '=', carry_ids.ids)], 'context': { 'partner_id': self.partner_id.id }, 'views': [(view_id, 'tree'), (view_id_form, 'form')], 'view_id': [view_id], } @api.multi # 结算 def btn_squaring_up(self): settlement_obj = self.env['qdodoo.car.settlement'] settlement_ids = settlement_obj.search([('sale_id', '=', self.id)]) result = self.env['ir.model.data'].get_object_reference( 'qdodoo_car_import_trade', 'tree_qdodoo_car_settlement') view_id = result and result[1] or False result_form = self.env['ir.model.data'].get_object_reference( 'qdodoo_car_import_trade', 'form_qdodoo_car_settlement') view_id_form = result_form and result_form[1] or False return { 'name': _('结算单'), 'view_type': 'form', "view_mode": 'tree,form', 'res_model': 'qdodoo.car.settlement', 'type': 'ir.actions.act_window', 'domain': [('id', '=', settlement_ids.ids)], 'views': [(view_id, 'tree'), (view_id_form, 'form')], 'view_id': [view_id], } @api.one # 取消 def btn_cancel(self): return self.write({'state': 'cancel'}) @api.one # 异常 def btn_unusual(self): return self.write({'state': 'no_normal'}) @api.one # 恢复执行 def btn_perform(self): return self.write({'state': 'doing'}) @api.multi # 查看车辆档案 def btn_car_information(self): information_obj = self.env['qdodoo.car.information'] information_ids = information_obj.search([('sale_contract', '=', self.id)]) result = self.env['ir.model.data'].get_object_reference( 'qdodoo_car_import_trade', 'tree_qdodoo_car_information') view_id = result and result[1] or False result_form = self.env['ir.model.data'].get_object_reference( 'qdodoo_car_import_trade', 'form_qdodoo_car_information') view_id_form = result_form and result_form[1] or False return { 'name': _('车辆档案'), 'view_type': 'form', "view_mode": 'tree,form', 'res_model': 'qdodoo.car.information', 'type': 'ir.actions.act_window', 'domain': [('id', 'in', information_ids.ids)], 'views': [(view_id, 'tree'), (view_id_form, 'form')], 'view_id': [view_id], } @api.multi # 查看外贸合同 def btn_purchase_contract(self): purchase_obj = self.env['qdodoo.car.purchase.contract'] purchase_ids = purchase_obj.search([('contract_id', '=', self.id)]) result = self.env['ir.model.data'].get_object_reference( 'qdodoo_car_import_trade', 'tree_qdodoo_car_purchase_contract') view_id = result and result[1] or False result_form = self.env['ir.model.data'].get_object_reference( 'qdodoo_car_import_trade', 'form_qdodoo_car_purchase_contract') view_id_form = result_form and result_form[1] or False return { 'name': _('外贸合同'), 'view_type': 'form', "view_mode": 'tree,form', 'res_model': 'qdodoo.car.purchase.contract', 'type': 'ir.actions.act_window', 'domain': [('id', 'in', purchase_ids.ids)], 'views': [(view_id, 'tree'), (view_id_form, 'form')], 'view_id': [view_id], }
class hrz_kardex_report(models.Model): _name = "sale.order.advance.report" _description = u"Por fechas, Por vehículo, Por dueños, Por deudas" cliente_id = fields.Many2one('res.partner','Cliente', domain = [('is_insurance','=',False), ('customer','=',True)]) seguro_id = fields.Many2one('res.partner','Aseguradora', domain = [('is_insurance','=',True), ('customer','=',True)]) vehiculo_id = fields.Many2one('vehicle','Vehiculo') date_from = fields.Date("Fecha inicio") date_to = fields.Date("Fecha Fin") entregado = fields.Boolean('Entregado') sin_entregar = fields.Boolean('Sin entregar') ordenes = [] def _print_report(self, cr, uid, ids, data, context=None): raise (_('Error!'), _('Not implemented.')) def get_total(self): print "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" sumTotal=0.0 sumAbono=0.0 sumSaldo=0.0 if self.ordenes == []: self.ordenes = self.get_orders() for line in self.ordenes: sumTotal+=line.amount_total sumAbono+=line.abono sumSaldo+=line.saldo return dict({"sumTotal":sumTotal,"sumAbono":sumAbono,"sumSaldo":sumSaldo}) def get_orders(self): #filtra todos los movimientos sean del producto escogido en wl wizard y que se encuentre entre las fechas establecidas #movimientos = self.env['sale.order'].search([('cliente_id', '=', self.cliente_id),('seguro_id', '=', self.seguro_id),('create_date', '<', (datetime.strptime(self.date_to, '%Y-%m-%d')+ timedelta(days=1)).strftime('%Y-%m-%d'))]) #filtros print "**********************************************************************" orden = None if self.ordenes == []: filtro = [] if self.cliente_id: filtro.append(('cliente_id', '=', self.cliente_id.id)) if self.seguro_id: filtro.append(('partner_id', '=', self.seguro_id.id)) if self.vehiculo_id: filtro.append(('vehiculo_id', '=', self.vehiculo_id.id))###########33aqui me quede if self.date_from: filtro.append(('date_order', '>=', self.date_from)) if self.date_to: filtro.append(('date_order', '<', (datetime.strptime(self.date_to, '%Y-%m-%d')+ timedelta(days=1)).strftime('%Y-%m-%d'))) if self.entregado: filtro.append(('state', '=', ('done'))) if self.sin_entregar: filtro.append(('state', 'in',('insurance_setting', 'no_insurance_setting', 'waiting_date', 'progress', 'manual', 'shipping_except', 'invoice_except',))) #('create_date', '>=', self.date_from), #('create_date', '<', (datetime.strptime(self.date_to, '%Y-%m-%d')+ timedelta(days=1)).strftime('%Y-%m-%d'))] #raise osv.except_osv('1',filtro) orden = self.env['sale.order'].search(filtro,order="partner_id asc, cliente_id asc") self.ordenes = orden return self.ordenes @api.multi def check_report(self): #raise osv.except_osv('Error!',str(self.get_total())) #data = {} #data['form']['landscape'] = True return self.env['report'].get_action(self, 'manta_auto_personalizacion.report_sale_order_advance') return True
class event_ticket(models.Model): _inherit = 'event.event.ticket' is_discovery_meeting_event = fields.Boolean( string='Discovery Meeting Event', store=True)
class IfrsLines(models.Model): _name = 'ifrs.lines' _order = 'ifrs_id, sequence' def _get_ifrs_query(self, cr, uid, brw, context=None): """ Fetches a semi-query to be provided as context into aml""" context = dict(context or {}) query = '' if not brw.filter_id: return query args = eval(brw.filter_id.domain) query = self.pool['account.move.line']._where_calc( cr, uid, args, context=context) where_clause, where_clause_params = query.get_sql()[1:] where_clause = where_clause.replace('account_move_line', 'l') query = cr.mogrify(where_clause, where_clause_params) return query def _get_sum_total( self, cr, uid, brw, operand, number_month=None, one_per=False, bag=None, context=None): """ Calculates the sum of the line total_ids & operand_ids the current ifrs.line @param number_month: period to compute """ context = context and dict(context) or {} res = 0 # If the report is two or twelve columns, will choose the field needed # to make the sum if context.get('whole_fy', False) or one_per: field_name = 'ytd' else: field_name = 'period_%s' % str(number_month) # It takes the sum of the total_ids & operand_ids for ttt in getattr(brw, operand): res += bag[ttt.id].get(field_name, 0.0) return res def _get_sum_detail(self, cr, uid, ids=None, number_month=None, context=None): """ Calculates the amount sum of the line type == 'detail' @param number_month: periodo a calcular """ fy_obj = self.pool.get('account.fiscalyear') period_obj = self.pool.get('account.period') context = context and dict(context) or {} cx = context.copy() res = 0.0 if not cx.get('fiscalyear'): cx['fiscalyear'] = fy_obj.find(cr, uid) fy_id = cx['fiscalyear'] brw = self.browse(cr, uid, ids) # custom date_domain = [] if brw.acc_val == 'init': if cx.get('whole_fy', False): cx['periods'] = period_obj.search(cr, uid, [ ('fiscalyear_id', '=', fy_id), ('special', '=', True)]) # custom date_domain += [('date_maturity', '<', fy_obj.browse(cr, uid, fy_id).date_start)] else: period_from = period_obj.search(cr, uid, [ ('fiscalyear_id', '=', fy_id), ('special', '=', True)]) # Case when the period_from is the first non-special period # of the fiscalyear if period_obj.browse(cr, uid, cx['period_from']).date_start == \ fy_obj.browse(cr, uid, fy_id).date_start: cx['period_to'] = period_from[0] else: cx['period_to'] = period_obj.previous( cr, uid, cx['period_from']) cx['period_from'] = period_from[0] # custom date_domain += [('date_maturity', '<', period_obj.browse(cr, uid, cx['period_from']).date_start)] elif brw.acc_val == 'var': # it is going to be the one sent by the previous cx if cx.get('whole_fy', False): cx['periods'] = period_obj.search(cr, uid, [ ('fiscalyear_id', '=', fy_id), ('special', '=', False)]) # custom date_domain += [('date_maturity', '>=', fy_obj.browse(cr, uid, fy_id).date_start)] date_domain += [('date_maturity', '<=', fy_obj.browse(cr, uid, fy_id).date_stop)] else: date_domain += [('date_maturity', '>=', period_obj.browse(cr, uid, cx['period_from']).date_start)] date_domain += [('date_maturity', '<=', period_obj.browse(cr, uid, cx['period_to']).date_stop)] else: # it is going to be from the fiscalyear's beginning if cx.get('whole_fy', False): cx['periods'] = period_obj.search(cr, uid, [ ('fiscalyear_id', '=', fy_id)]) # custom date_domain += [('date_maturity', '<=', fy_obj.browse(cr, uid, fy_id).date_stop)] else: period_from = period_obj.search(cr, uid, [ ('fiscalyear_id', '=', fy_id), ('special', '=', True)]) cx['period_from'] = period_from[0] cx['periods'] = \ period_obj.build_ctx_periods(cr, uid, cx['period_from'], cx['period_to']) # custom date_domain += [('date_maturity', '<=', fy_obj.browse(cr, uid, fy_id).date_stop)] if brw.type == 'detail': # Si es de tipo detail # If we have to only take into account a set of Journals cx['journal_ids'] = [aj_brw.id for aj_brw in brw.journal_ids] cx['analytic'] = [an.id for an in brw.analytic_ids] cx['ifrs_tax'] = [tx.id for tx in brw.tax_code_ids] cx['ifrs_partner'] = [p_brw.id for p_brw in brw.partner_ids] cx['ifrs_query'] = self._get_ifrs_query(cr, uid, brw, context) # NOTE: This feature is not yet been implemented # cx['partner_detail'] = cx.get('partner_detail') # Refreshing record with new context brw = self.browse(cr, uid, ids, context=cx) for aa in brw.cons_ids: # Se hace la sumatoria de la columna balance, credito o debito. # Dependiendo de lo que se escoja en el wizard if brw.value == 'debit': res += aa.debit elif brw.value == 'credit': res += aa.credit else: res += aa.balance # custom if brw.ifrs_id.arap_account_ids and aa.id in brw.ifrs_id.arap_account_ids._get_children_and_consol(): domain = [('account_id', 'in', aa._get_children_and_consol()), ('date_maturity', '!=', False)] + date_domain aml_ids = self.pool.get('account.move.line').search(cr, uid, domain) if brw.value in ('debit', 'balance'): res += sum([aml.amount_residual if aml.amount_residual > 0 else 0 for aml in self.pool.get('account.move.line').browse(cr, uid, aml_ids)]) else: res += sum([-aml.amount_residual if aml.amount_residual < 0 else 0 for aml in self.pool.get('account.move.line').browse(cr, uid, aml_ids)]) if brw.ifrs_id.forecast_ids: domain = [('account_id', 'in', aa._get_children_and_consol()), ('forecast_id', 'in', [f.id for f in brw.ifrs_id.forecast_ids])] + date_domain afl_ids = self.pool.get('account.forecast.line').search(cr, uid, domain) if brw.value in ('debit', 'balance'): res += sum([afl.amount if afl.amount > 0 else 0 for afl in self.pool.get('account.forecast.line').browse(cr, uid, afl_ids)]) else: res += sum([-afl.amount if afl.amount < 0 else 0 for afl in self.pool.get('account.forecast.line').browse(cr, uid, afl_ids)]) return res def _get_logical_operation(self, cr, uid, brw, ilf, irg, context=None): def result(brw, ifn, ilf, irg): if getattr(brw, ifn) == 'subtract': res = ilf - irg elif getattr(brw, ifn) == 'addition': res = ilf + irg elif getattr(brw, ifn) == 'lf': res = ilf elif getattr(brw, ifn) == 'rg': res = irg elif getattr(brw, ifn) == 'zr': res = 0.0 return res context = dict(context or {}) fnc = getattr(op, brw.logical_operation) if fnc(ilf, irg): res = result(brw, 'logical_true', ilf, irg) else: res = result(brw, 'logical_false', ilf, irg) return res def _get_grand_total( self, cr, uid, ids, number_month=None, one_per=False, bag=None, context=None): """ Calculates the amount sum of the line type == 'total' @param number_month: periodo a calcular """ fy_obj = self.pool.get('account.fiscalyear') context = context and dict(context) or {} cx = context.copy() res = 0.0 if not cx.get('fiscalyear'): cx['fiscalyear'] = fy_obj.find(cr, uid) brw = self.browse(cr, uid, ids) res = self._get_sum_total( cr, uid, brw, 'total_ids', number_month, one_per=one_per, bag=bag, context=cx) if brw.operator in ('subtract', 'condition', 'percent', 'ratio', 'product'): so = self._get_sum_total( cr, uid, brw, 'operand_ids', number_month, one_per=one_per, bag=bag, context=cx) if brw.operator == 'subtract': res -= so elif brw.operator == 'condition': res = self._get_logical_operation(cr, uid, brw, res, so, context=cx) elif brw.operator == 'percent': res = so != 0 and (100 * res / so) or 0.0 elif brw.operator == 'ratio': res = so != 0 and (res / so) or 0.0 elif brw.operator == 'product': res = res * so return res def _get_constant(self, cr, uid, ids=None, number_month=None, context=None): """ Calculates the amount sum of the line of constant @param number_month: periodo a calcular """ cx = context or {} brw = self.browse(cr, uid, ids, context=cx) if brw.constant_type == 'constant': return brw.constant fy_obj = self.pool.get('account.fiscalyear') period_obj = self.pool.get('account.period') if not cx.get('fiscalyear'): cx['fiscalyear'] = fy_obj.find(cr, uid, dt=None, context=cx) if not cx.get('period_from', False) and not cx.get('period_to', False): if context.get('whole_fy', False): cx['period_from'] = period_obj.find_special_period( cr, uid, cx['fiscalyear']) cx['period_to'] = period_obj.search( cr, uid, [('fiscalyear_id', '=', cx['fiscalyear'])])[-1] if brw.constant_type == 'period_days': res = period_obj._get_period_days( cr, uid, cx['period_from'], cx['period_to']) elif brw.constant_type == 'fy_periods': res = fy_obj._get_fy_periods(cr, uid, cx['fiscalyear']) elif brw.constant_type == 'fy_month': res = fy_obj._get_fy_month(cr, uid, cx[ 'fiscalyear'], cx['period_to']) elif brw.constant_type == 'number_customer': res = self._get_number_customer_portfolio(cr, uid, ids, cx[ 'fiscalyear'], cx['period_to'], cx) return res def exchange(self, cr, uid, ids, from_amount, to_currency_id, from_currency_id, exchange_date, context=None): context = context and dict(context) or {} if from_currency_id == to_currency_id: return from_amount curr_obj = self.pool.get('res.currency') context['date'] = exchange_date return curr_obj.compute(cr, uid, from_currency_id, to_currency_id, from_amount, context=context) def _get_amount_value( self, cr, uid, ids, ifrs_line=None, period_info=None, fiscalyear=None, exchange_date=None, currency_wizard=None, number_month=None, target_move=None, pdx=None, undefined=None, two=None, one_per=False, bag=None, context=None): """ Returns the amount corresponding to the period of fiscal year @param ifrs_line: linea a calcular monto @param period_info: informacion de los periodos del fiscal year @param fiscalyear: selected fiscal year @param exchange_date: date of change currency @param currency_wizard: currency in the report @param number_month: period number @param target_move: target move to consider """ context = context and dict(context) or {} # TODO: Current Company's Currency shall be used: the one on wizard from_currency_id = ifrs_line.ifrs_id.company_id.currency_id.id to_currency_id = currency_wizard if number_month: if two: context = { 'period_from': number_month, 'period_to': number_month} else: period_id = period_info[number_month][1] context = {'period_from': period_id, 'period_to': period_id} else: context = {'whole_fy': True} # NOTE: This feature is not yet been implemented # context['partner_detail'] = pdx context['fiscalyear'] = fiscalyear context['state'] = target_move if ifrs_line.type == 'detail': res = self._get_sum_detail( cr, uid, ifrs_line.id, number_month, context=context) elif ifrs_line.type == 'total': res = self._get_grand_total( cr, uid, ifrs_line.id, number_month, one_per=one_per, bag=bag, context=context) elif ifrs_line.type == 'constant': res = self._get_constant(cr, uid, ifrs_line.id, number_month, context=context) else: res = 0.0 if ifrs_line.type == 'detail': res = self.exchange( cr, uid, ids, res, to_currency_id, from_currency_id, exchange_date, context=context) return res def _get_dict_amount_with_operands( self, cr, uid, ids, ifrs_line, period_info=None, fiscalyear=None, exchange_date=None, currency_wizard=None, month_number=None, target_move=None, pdx=None, undefined=None, two=None, one_per=False, bag=None, context=None): """ Integrate operand_ids field in the calculation of the amounts for each line @param ifrs_line: linea a calcular monto @param period_info: informacion de los periodos del fiscal year @param fiscalyear: selected fiscal year @param exchange_date: date of change currency @param currency_wizard: currency in the report @param month_number: period number @param target_move: target move to consider """ context = dict(context or {}) direction = ifrs_line.inv_sign and -1.0 or 1.0 res = {} for number_month in range(1, 13): field_name = 'period_%(month)s' % dict(month=number_month) bag[ifrs_line.id][field_name] = self._get_amount_value( cr, uid, ids, ifrs_line, period_info, fiscalyear, exchange_date, currency_wizard, number_month, target_move, pdx, undefined, two, one_per=one_per, bag=bag, context=context) * direction res[number_month] = bag[ifrs_line.id][field_name] return res def _get_amount_with_operands( self, cr, uid, ids, ifrs_l, period_info=None, fiscalyear=None, exchange_date=None, currency_wizard=None, number_month=None, target_move=None, pdx=None, undefined=None, two=None, one_per=False, bag=None, context=None): """ Integrate operand_ids field in the calculation of the amounts for each line @param ifrs_line: linea a calcular monto @param period_info: informacion de los periodos del fiscal year @param fiscalyear: selected fiscal year @param exchange_date: date of change currency @param currency_wizard: currency in the report @param number_month: period number @param target_move: target move to consider """ context = context and dict(context) or {} if not number_month: context = {'whole_fy': True} res = self._get_amount_value( cr, uid, ids, ifrs_l, period_info, fiscalyear, exchange_date, currency_wizard, number_month, target_move, pdx, undefined, two, one_per=one_per, bag=bag, context=context) res = ifrs_l.inv_sign and (-1.0 * res) or res bag[ifrs_l.id]['ytd'] = res return res def _get_number_customer_portfolio(self, cr, uid, ids, fyr, period, context=None): ifrs_brw = self.browse(cr, uid, ids, context=context) company_id = ifrs_brw.ifrs_id.company_id.id if context.get('whole_fy', False): period_fy = [('period_id.fiscalyear_id', '=', fyr), ('period_id.special', '=', False)] else: period_fy = [('period_id', '=', period)] invoice_obj = self.pool.get('account.invoice') invoice_ids = invoice_obj.search(cr, uid, [ ('type', '=', 'out_invoice'), ('state', 'in', ('open', 'paid',)), ('company_id', '=', company_id)] + period_fy) partner_number = \ set([inv.partner_id.id for inv in invoice_obj.browse(cr, uid, invoice_ids, context=context)]) return len(list(partner_number)) def onchange_sequence(self, cr, uid, ids, sequence, context=None): context = context and dict(context) or {} return {'value': {'priority': sequence}} @api.returns('self') def _get_default_help_bool(self): ctx = dict(self._context) return ctx.get('ifrs_help', True) @api.returns('self') def _get_default_sequence(self): ctx = dict(self._context) res = 0 if not ctx.get('ifrs_id'): return res + 10 ifrs_lines_ids = self.search([('ifrs_id', '=', ctx['ifrs_id'])]) if ifrs_lines_ids: res = max(line.sequence for line in ifrs_lines_ids) return res + 10 def onchange_type_without(self, cr, uid, ids, ttype, operator, context=None): context = context and dict(context) or {} res = {} if ttype == 'total' and operator == 'without': res = {'value': {'operand_ids': []}} return res def write(self, cr, uid, ids, vals, context=None): ids = isinstance(ids, (int, long)) and [ids] or ids res = super(IfrsLines, self).write(cr, uid, ids, vals) for ifrs_line in self.pool.get('ifrs.lines').browse(cr, uid, ids): if ifrs_line.type == 'total' and ifrs_line.operator == 'without': vals['operand_ids'] = [(6, 0, [])] super(IfrsLines, self).write(cr, uid, ifrs_line.id, vals) return res help = fields.Boolean( string='Show Help', copy=False, related='ifrs_id.help', default=_get_default_help_bool, help='Allows you to show the help in the form') # Really!!! A repeated field with same functionality! This was done due # to the fact that web view everytime that sees sequence tries to allow # you to change the values and this feature here is undesirable. sequence = fields.Integer( string='Sequence', default=_get_default_sequence, help=('Indicates the order of the line in the report. The sequence ' 'must be unique and unrepeatable')) priority = fields.Integer( string='Sequence', default=_get_default_sequence, related='sequence', help=('Indicates the order of the line in the report. The sequence ' 'must be unique and unrepeatable')) name = fields.Char( string='Name', size=128, required=True, translate=True, help=('Line name in the report. This name can be translatable, if ' 'there are multiple languages loaded it can be translated')) type = fields.Selection( [('abstract', 'Abstract'), ('detail', 'Detail'), ('constant', 'Constant'), ('total', 'Total')], string='Type', required=True, default='abstract', help=('Line type of report: \n-Abstract(A),\n-Detail(D), ' '\n-Constant(C),\n-Total(T)')) constant = fields.Float( string='Constant', help=('Fill this field with your own constant that will be used ' 'to compute in your other lines'), readonly=False) constant_type = fields.Selection( [('constant', 'My Own Constant'), ('period_days', 'Days of Period'), ('fy_periods', "FY's Periods"), ('fy_month', "FY's Month"), ('number_customer', "Number of customers* in portfolio")], string='Constant Type', required=False, help='Constant Type') ifrs_id = fields.Many2one( 'ifrs.ifrs', string='IFRS', ondelete='cascade', required=True) company_id = fields.Many2one( 'res.company', string='Company', related='ifrs_id.company_id', store=True) amount = fields.Float( string='Amount', readonly=True, help=('This field will update when you click the compute button in ' 'the IFRS doc form')) cons_ids = fields.Many2many( 'account.account', 'ifrs_account_rel', 'ifrs_lines_id', 'account_id', string='Consolidated Accounts') journal_ids = fields.Many2many( 'account.journal', 'ifrs_journal_rel', 'ifrs_lines_id', 'journal_id', string='Journals', required=False) analytic_ids = fields.Many2many( 'account.analytic.account', 'ifrs_analytic_rel', 'ifrs_lines_id', 'analytic_id', string='Consolidated Analytic Accounts') partner_ids = fields.Many2many( 'res.partner', 'ifrs_partner_rel', 'ifrs_lines_id', 'partner_id', string='Partners') tax_code_ids = fields.Many2many( 'account.tax.code', 'ifrs_tax_rel', 'ifrs_lines_id', 'tax_code_id', string='Tax Codes') filter_id = fields.Many2one( 'ir.filters', string='Custom Filter', ondelete='set null', domain=("[('model_id','=','account.move.line')]")) parent_id = fields.Many2one( 'ifrs.lines', string='Parent', ondelete='set null', domain=("[('ifrs_id','=',parent.id)," "('type','=','total'),('id','!=',id)]")) operand_ids = fields.Many2many( 'ifrs.lines', 'ifrs_operand_rel', 'ifrs_parent_id', 'ifrs_child_id', string='Second Operand') operator = fields.Selection( [('subtract', 'Subtraction'), ('condition', 'Conditional'), ('percent', 'Percentage'), ('ratio', 'Ratio'), ('product', 'Product'), ('without', 'First Operand Only')], string='Operator', required=False, default='without', help='Leaving blank will not take into account Operands') logical_operation = fields.Selection( LOGICAL_OPERATIONS, string='Logical Operations', required=False, help=('Select type of Logical Operation to perform with First ' '(Left) and Second (Right) Operand')) logical_true = fields.Selection( LOGICAL_RESULT, string='Logical True', required=False, help=('Value to return in case Comparison is True')) logical_false = fields.Selection( LOGICAL_RESULT, string='Logical False', required=False, help=('Value to return in case Comparison is False')) comparison = fields.Selection( [('subtract', 'Subtraction'), ('percent', 'Percentage'), ('ratio', 'Ratio'), ('without', 'No Comparison')], string='Make Comparison', required=False, default='without', help=('Make a Comparison against the previous period.\nThat is, ' 'period X(n) minus period X(n-1)\nLeaving blank will not ' 'make any effects')) acc_val = fields.Selection( [('init', 'Initial Values'), ('var', 'Variation in Periods'), ('fy', ('Ending Values'))], string='Accounting Span', required=False, default='fy', help='Leaving blank means YTD') value = fields.Selection( [('debit', 'Debit'), ('credit', 'Credit'), ('balance', 'Balance')], string='Accounting Value', required=False, default='balance', help='Leaving blank means Balance') total_ids = fields.Many2many( 'ifrs.lines', 'ifrs_lines_rel', 'parent_id', 'child_id', string='First Operand') inv_sign = fields.Boolean( string='Change Sign to Amount', default=False, copy=True, help='Allows you to show the help in the form') invisible = fields.Boolean( string='Invisible', default=False, copy=True, help='Allows whether the line of the report is printed or not') comment = fields.Text( string='Comments/Question', help='Comments or questions about this ifrs line') # _sql_constraints = [ # ('sequence_ifrs_id_unique', 'unique(ifrs_id)', # 'The sequence already have been set in another IFRS line')] def _get_level(self, cr, uid, lll, tree, level=1, context=None): """ Calcula los niveles de los ifrs.lines, tomando en cuenta que sera un mismo arbol para los campos total_ids y operand_ids. @param lll: objeto a un ifrs.lines @param level: Nivel actual de la recursion @param tree: Arbol de dependencias entre lineas construyendose """ context = context and dict(context) or {} if not tree.get(level): tree[level] = {} # The search through level should be backwards from the deepest level # to the outmost level levels = tree.keys() levels.sort() levels.reverse() xlevel = False for nnn in levels: xlevel = isinstance(tree[nnn].get(lll.id), (set)) and nnn or xlevel if not xlevel: tree[level][lll.id] = set() elif xlevel < level: tree[level][lll.id] = tree[xlevel][lll.id] del tree[xlevel][lll.id] else: # xlevel >= level return True for jjj in set(lll.total_ids + lll.operand_ids): tree[level][lll.id].add(jjj.id) self._get_level(cr, uid, jjj, tree, level + 1, context=context) return True
class MagentoBackend(models.Model): _name = 'magento.backend' _description = 'Magento Backend' _inherit = 'connector.backend' _backend_type = 'magento' @api.model def select_versions(self): """ Available versions in the backend. Can be inherited to add custom versions. Using this method to add a version from an ``_inherit`` does not constrain to redefine the ``version`` field in the ``_inherit`` model. """ return [('1.7', '1.7+')] @api.model def _get_stock_field_id(self): field = self.env['ir.model.fields'].search( [('model', '=', 'product.product'), ('name', '=', 'virtual_available')], limit=1) return field version = fields.Selection(selection='select_versions', required=True) location = fields.Char( string='Location', required=True, help="Url to magento application", ) admin_location = fields.Char(string='Admin Location') use_custom_api_path = fields.Boolean( string='Custom Api Path', help="The default API path is '/index.php/api/xmlrpc'. " "Check this box if you use a custom API path, in that case, " "the location has to be completed with the custom API path ", ) username = fields.Char( string='Username', help="Webservice user", ) password = fields.Char( string='Password', help="Webservice password", ) use_auth_basic = fields.Boolean( string='Use HTTP Auth Basic', help="Use a Basic Access Authentication for the API. " "The Magento server could be configured to restrict access " "using a HTTP authentication based on a username and " "a password.", ) auth_basic_username = fields.Char( string='Basic Auth. Username', help="Basic access authentication web server side username", ) auth_basic_password = fields.Char( string='Basic Auth. Password', help="Basic access authentication web server side password", ) sale_prefix = fields.Char( string='Sale Prefix', help="A prefix put before the name of imported sales orders.\n" "For instance, if the prefix is 'mag-', the sales " "order 100000692 in Magento, will be named 'mag-100000692' " "in OpenERP.", ) warehouse_id = fields.Many2one( comodel_name='stock.warehouse', string='Warehouse', required=True, help='Warehouse used to compute the ' 'stock quantities.', ) company_id = fields.Many2one( comodel_name='res.company', related='warehouse_id.company_id', string='Company', readonly=True, ) website_ids = fields.One2many( comodel_name='magento.website', inverse_name='backend_id', string='Website', readonly=True, ) default_lang_id = fields.Many2one( comodel_name='res.lang', string='Default Language', help="If a default language is selected, the records " "will be imported in the translation of this language.\n" "Note that a similar configuration exists " "for each storeview.", ) default_category_id = fields.Many2one( comodel_name='product.category', string='Default Product Category', help='If a default category is selected, products imported ' 'without a category will be linked to it.', ) # TODO? add a field `auto_activate` -> activate a cron import_products_from_date = fields.Datetime( string='Import products from date', ) import_categories_from_date = fields.Datetime( string='Import categories from date', ) product_stock_field_id = fields.Many2one( comodel_name='ir.model.fields', string='Stock Field', default=_get_stock_field_id, domain="[('model', 'in', ['product.product', 'product.template'])," " ('ttype', '=', 'float')]", help="Choose the field of the product which will be used for " "stock inventory updates.\nIf empty, Quantity Available " "is used.", ) product_binding_ids = fields.One2many( comodel_name='magento.product.product', inverse_name='backend_id', string='Magento Products', readonly=True, ) account_analytic_id = fields.Many2one( comodel_name='account.analytic.account', string='Analytic account', help='If specified, this analytic account will be used to fill the ' 'field on the sale order created by the connector. The value can ' 'also be specified on website or the store or the store view.' ) fiscal_position_id = fields.Many2one( comodel_name='account.fiscal.position', string='Fiscal position', help='If specified, this fiscal position will be used to fill the ' 'field fiscal position on the sale order created by the connector.' 'The value can also be specified on website or the store or the ' 'store view.' ) _sql_constraints = [ ('sale_prefix_uniq', 'unique(sale_prefix)', "A backend with the same sale prefix already exists") ] @api.multi def check_magento_structure(self): """ Used in each data import. Verify if a website exists for each backend before starting the import. """ for backend in self: websites = backend.website_ids if not websites: backend.synchronize_metadata() return True @api.multi def synchronize_metadata(self): try: session = ConnectorSession.from_env(self.env) for backend in self: for model in ('magento.website', 'magento.store', 'magento.storeview'): # import directly, do not delay because this # is a fast operation, a direct return is fine # and it is simpler to import them sequentially import_batch(session, model, backend.id) return True except Exception as e: _logger.error(e.message, exc_info=True) raise UserError( _(u"Check your configuration, we can't get the data. " u"Here is the error:\n%s") % str(e).decode('utf-8', 'ignore')) @api.multi def import_partners(self): """ Import partners from all websites """ for backend in self: backend.check_magento_structure() backend.website_ids.import_partners() return True @api.multi def import_sale_orders(self): """ Import sale orders from all store views """ storeview_obj = self.env['magento.storeview'] storeviews = storeview_obj.search([('backend_id', 'in', self.ids)]) storeviews.import_sale_orders() return True @api.multi def import_customer_groups(self): session = ConnectorSession(self.env.cr, self.env.uid, context=self.env.context) for backend in self: backend.check_magento_structure() import_batch.delay(session, 'magento.res.partner.category', backend.id) return True @api.multi def _import_from_date(self, model, from_date_field): session = ConnectorSession(self.env.cr, self.env.uid, context=self.env.context) import_start_time = datetime.now() for backend in self: backend.check_magento_structure() from_date = getattr(backend, from_date_field) if from_date: from_date = fields.Datetime.from_string(from_date) else: from_date = None import_batch.delay(session, model, backend.id, filters={'from_date': from_date, 'to_date': import_start_time}) # Records from Magento are imported based on their `created_at` # date. This date is set on Magento at the beginning of a # transaction, so if the import is run between the beginning and # the end of a transaction, the import of a record may be # missed. That's why we add a small buffer back in time where # the eventually missed records will be retrieved. This also # means that we'll have jobs that import twice the same records, # but this is not a big deal because they will be skipped when # the last `sync_date` is the same. next_time = import_start_time - timedelta(seconds=IMPORT_DELTA_BUFFER) next_time = fields.Datetime.to_string(next_time) self.write({from_date_field: next_time}) @api.multi def import_product_categories(self): self._import_from_date('magento.product.category', 'import_categories_from_date') return True @api.multi def import_product_product(self): self._import_from_date('magento.product.product', 'import_products_from_date') return True @api.multi def _domain_for_update_product_stock_qty(self): return [ ('backend_id', 'in', self.ids), ('type', '!=', 'service'), ('no_stock_sync', '=', False), ] @api.multi def update_product_stock_qty(self): mag_product_obj = self.env['magento.product.product'] domain = self._domain_for_update_product_stock_qty() magento_products = mag_product_obj.search(domain) magento_products.recompute_magento_qty() return True @api.model def _magento_backend(self, callback, domain=None): if domain is None: domain = [] backends = self.search(domain) if backends: getattr(backends, callback)() @api.model def _scheduler_import_sale_orders(self, domain=None): self._magento_backend('import_sale_orders', domain=domain) @api.model def _scheduler_import_customer_groups(self, domain=None): self._magento_backend('import_customer_groups', domain=domain) @api.model def _scheduler_import_partners(self, domain=None): self._magento_backend('import_partners', domain=domain) @api.model def _scheduler_import_product_categories(self, domain=None): self._magento_backend('import_product_categories', domain=domain) @api.model def _scheduler_import_product_product(self, domain=None): self._magento_backend('import_product_product', domain=domain) @api.model def _scheduler_update_product_stock_qty(self, domain=None): self._magento_backend('update_product_stock_qty', domain=domain) @api.multi def output_recorder(self): """ Utility method to output a file containing all the recorded requests / responses with Magento. Used to generate test data. Should be called with ``erppeek`` for instance. """ from .unit.backend_adapter import output_recorder import os import tempfile fmt = '%Y-%m-%d-%H-%M-%S' timestamp = datetime.now().strftime(fmt) filename = 'output_%s_%s' % (self.env.cr.dbname, timestamp) path = os.path.join(tempfile.gettempdir(), filename) output_recorder(path) return path
class MrpProductionWorkcenterLine(models.Model): _inherit = 'mrp.production.workcenter.line' @api.one def _ready_materials(self): self.is_material_ready = True if self.product_line: moves = self.env['stock.move'].search([('work_order', '=', self.id) ]) self.is_material_ready = not any( x not in ('assigned', 'cancel', 'done') for x in moves.mapped('state')) @api.multi @api.depends('routing_wc_line') def _compute_possible_workcenters(self): for line in self: line.possible_workcenters = line.mapped( 'routing_wc_line.op_wc_lines.workcenter') product_line = fields.One2many(comodel_name='mrp.production.product.line', inverse_name='work_order', string='Product Lines') routing_wc_line = fields.Many2one(comodel_name='mrp.routing.workcenter', string='Routing WC Line') do_production = fields.Boolean(string='Produce here') time_start = fields.Float(string="Time Start") time_stop = fields.Float(string="Time Stop") move_lines = fields.One2many(comodel_name='stock.move', inverse_name='work_order', string='Moves') is_material_ready = fields.Boolean(string='Materials Ready', compute="_ready_materials") possible_workcenters = fields.Many2many( comodel_name="mrp.workcenter", compute="_compute_possible_workcenters") workcenter_id = fields.Many2one( domain="[('id', 'in', possible_workcenters[0][2])]") @api.one def action_assign(self): self.move_lines.action_assign() @api.one def force_assign(self): self.move_lines.force_assign() @api.multi def _load_mo_date_planned(self, production, date_planned): if date_planned < production.date_planned: production.write({'date_planned': date_planned}) return True return False @api.model def create(self, vals): production_obj = self.env['mrp.production'] dp = vals.get('date_planned', False) production_id = vals.get('production_id', False) if dp and production_id: production = production_obj.browse(production_id) self._load_mo_date_planned(production, dp) return super(MrpProductionWorkcenterLine, self).create(vals) @api.multi def write(self, vals, update=False): if vals.get('date_planned', False): dp = vals.get('date_planned') update = self._load_mo_date_planned(self.production_id, dp) res = super(MrpProductionWorkcenterLine, self).write(vals, update=update) return res def check_minor_sequence_operations(self): seq = self.sequence for operation in self.production_id.workcenter_lines: if operation.sequence < seq and operation.state != 'done': return False return True def check_operation_moves_state(self, states): for move_line in self.move_lines: if move_line.state not in states: return False return True def action_start_working(self): if self.routing_wc_line.previous_operations_finished and \ not self.check_minor_sequence_operations(): raise exceptions.Warning(_("Previous operations not finished")) if not self.check_operation_moves_state(['assigned', 'cancel', 'done' ]): raise exceptions.Warning( _("Missing materials to start the production")) if self.production_id.state in ('confirmed', 'ready'): self.production_id.state = 'in_production' return super(MrpProductionWorkcenterLine, self).action_start_working()
class SiiTax(models.Model): _inherit = 'account.tax' sii_code = fields.Integer('SII Code') sii_type = fields.Selection([('A', 'Anticipado'), ('R', 'Retención')], string="Tipo de impuesto para el SII") retencion = fields.Float(string="Valor retención", default=0.00) no_rec = fields.Boolean( string="Es No Recuperable" ) #esto es distinto al código no recuperable, depende del manejo de recuperación de impuesto activo_fijo = fields.Boolean(string="Activo Fijo", default=False) @api.v8 def compute_all(self, price_unit, currency=None, quantity=1.0, product=None, partner=None, discount=None): """ Returns all information required to apply taxes (in self + their children in case of a tax goup). We consider the sequence of the parent for group of taxes. Eg. considering letters as taxes and alphabetic order as sequence : [G, B([A, D, F]), E, C] will be computed as [A, D, F, C, E, G] RETURN: { 'total_excluded': 0.0, # Total without taxes 'total_included': 0.0, # Total with taxes 'taxes': [{ # One dict for each tax in self and their children 'id': int, 'name': str, 'amount': float, 'sequence': int, 'account_id': int, 'refund_account_id': int, 'analytic': boolean, }] } """ if len(self) == 0: company_id = self.env.user.company_id else: company_id = self[0].company_id if not currency: currency = company_id.currency_id taxes = [] # By default, for each tax, tax amount will first be computed # and rounded at the 'Account' decimal precision for each # PO/SO/invoice line and then these rounded amounts will be # summed, leading to the total amount for that tax. But, if the # company has tax_calculation_rounding_method = round_globally, # we still follow the same method, but we use a much larger # precision when we round the tax amount for each line (we use # the 'Account' decimal precision + 5), and that way it's like # rounding after the sum of the tax amounts of each line prec = currency.decimal_places base = round(price_unit * quantity, prec + 2) base = round(base, prec) tot_discount = round(base * ((discount or 0.0) / 100)) base -= tot_discount total_excluded = total_included = base if company_id.tax_calculation_rounding_method == 'round_globally' or not bool( self.env.context.get("round", True)): prec += 5 # Sorting key is mandatory in this case. When no key is provided, sorted() will perform a # search. However, the search method is overridden in account.tax in order to add a domain # depending on the context. This domain might filter out some taxes from self, e.g. in the # case of group taxes. for tax in self.sorted(key=lambda r: r.sequence): if tax.amount_type == 'group': ret = tax.children_tax_ids.compute_all(price_unit, currency, quantity, product, partner) total_excluded = ret['total_excluded'] base = ret['base'] total_included = ret['total_included'] tax_amount_retencion = ret['retencion'] tax_amount = total_included - total_excluded + tax_amount_retencion taxes += ret['taxes'] continue tax_amount = tax._compute_amount(base, price_unit, quantity, product, partner) if company_id.tax_calculation_rounding_method == 'round_globally' or not bool( self.env.context.get("round", True)): tax_amount = round(tax_amount, prec) else: tax_amount = currency.round(tax_amount) tax_amount_retencion = 0 if tax.sii_type in ['R']: tax_amount_retencion = tax._compute_amount_ret( base, price_unit, quantity, product, partner) if company_id.tax_calculation_rounding_method == 'round_globally' or not bool( self.env.context.get("round", True)): tax_amount_retencion = round(tax_amount_retencion, prec) if tax.price_include: total_excluded -= (tax_amount - tax_amount_retencion) total_included -= (tax_amount_retencion) base -= (tax_amount - tax_amount_retencion) else: total_included += (tax_amount - tax_amount_retencion) else: if tax.price_include: total_excluded -= tax_amount base -= tax_amount else: total_included += tax_amount # Keep base amount used for the current tax tax_base = base if tax.include_base_amount: base += tax_amount taxes.append({ 'id': tax.id, 'name': tax.with_context(**{ 'lang': partner.lang } if partner else {}).name, 'amount': tax_amount, 'retencion': tax_amount_retencion, 'base': tax_base, 'sequence': tax.sequence, 'account_id': tax.account_id.id, 'refund_account_id': tax.refund_account_id.id, 'analytic': tax.analytic, }) return { 'taxes': sorted(taxes, key=lambda k: k['sequence']), 'total_excluded': currency.round(total_excluded) if bool( self.env.context.get("round", True)) else total_excluded, 'total_included': currency.round(total_included) if bool( self.env.context.get("round", True)) else total_included, 'base': base, } def _compute_amount(self, base_amount, price_unit, quantity=1.0, product=None, partner=None): if self.amount_type == 'percent' and self.price_include: neto = base_amount / (1 + self.amount / 100) tax = base_amount - neto return tax return super(SiiTax, self)._compute_amount(base_amount, price_unit, quantity, product, partner) def _compute_amount_ret(self, base_amount, price_unit, quantity=1.0, product=None, partner=None): if self.amount_type == 'percent' and self.price_include: neto = base_amount / (1 + self.retencion / 100) tax = base_amount - neto return tax if (self.amount_type == 'percent' and not self.price_include) or (self.amount_type == 'division' and self.price_include): return base_amount * self.retencion / 100