Example #1
0
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!'))
Example #2
0
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
Example #4
0
class SaleOrder(models.Model):
    _inherit = 'sale.order'

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

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

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

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

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

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

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

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

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

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

    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],
        }
Example #6
0
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
Example #7
0
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
Example #10
0
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]
Example #11
0
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]
Example #12
0
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
Example #13
0
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
Example #14
0
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)
Example #15
0
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
Example #16
0
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
Example #17
0
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
Example #18
0
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
Example #19
0
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)
Example #20
0
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'))
    ]
Example #21
0
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
Example #22
0
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
Example #23
0
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)
Example #27
0
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
Example #28
0
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
Example #29
0
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()
Example #30
0
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