Ejemplo n.º 1
0
class stock_reportcustomer_model(osv.osv):
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _name = 'stock.reportcustomer.model'
    _description = 'Formulario de las Factuas del Cliente'
    _columns = {
        'name':
        fields.many2one('res.partner', 'Cliente'),
        'date':
        fields.date('Fecha Inicio', required=False),
        'date_end':
        fields.date('Fecha Fin', required=False),
        'reportcustomer_lines':
        fields.one2many('stock.reportcustomer.model.line', 'reportcustomer_id',
                        ' Detalle de los Productos'),
        'reportcustomer_invoice_lines':
        fields.one2many('stock.reportcustomer.invoices.line',
                        'reportcustomer_id', ' Detalle de los Productos'),
        'total_invoices':
        fields.float(
            'Monto Total Facturacion',
            digits=(14, 2),
        )
    }
    _defaults = {}
    _order = 'id desc'
Ejemplo n.º 2
0
class account_analytic_chart(osv.osv_memory):
    _name = 'account.analytic.chart'
    _description = 'Account Analytic Chart'

    _columns = {
        'from_date': fields.date('From'),
        'to_date': fields.date('To'),
    }

    def analytic_account_chart_open_window(self, cr, uid, ids, context=None):
        mod_obj = self.pool.get('ir.model.data')
        act_obj = self.pool.get('ir.actions.act_window')
        result_context = {}
        if context is None:
            context = {}
        result = mod_obj.get_object_reference(cr, uid, 'analytic',
                                              'action_analytic_account_form')
        id = result and result[1] or False
        result = act_obj.read(cr, uid, [id], context=context)[0]
        data = self.read(cr, uid, ids, [])[0]
        if data['from_date']:
            result_context.update({'from_date': data['from_date']})
        if data['to_date']:
            result_context.update({'to_date': data['to_date']})
        result['context'] = str(result_context)
        return result
Ejemplo n.º 3
0
class resource_calendar_attendance(osv.osv):
    _name = "resource.calendar.attendance"
    _description = "Work Detail"

    _columns = {
        'name':
        fields.char("Name", required=True),
        'dayofweek':
        fields.selection([('0', 'Monday'),
                          ('1', 'Tuesday'), ('2', 'Wednesday'),
                          ('3', 'Thursday'), ('4', 'Friday'),
                          ('5', 'Saturday'), ('6', 'Sunday')],
                         'Day of Week',
                         required=True,
                         select=True),
        'date_from':
        fields.date('Starting Date'),
        'date_to':
        fields.date('End Date'),
        'hour_from':
        fields.float('Work from',
                     required=True,
                     help="Start and End time of working.",
                     select=True),
        'hour_to':
        fields.float("Work to", required=True),
        'calendar_id':
        fields.many2one("resource.calendar",
                        "Resource's Calendar",
                        required=True),
    }

    _order = 'dayofweek, hour_from'

    _defaults = {'dayofweek': '0'}
class account_budget_crossvered_report(osv.osv_memory):

    _name = "account.budget.crossvered.report"
    _description = "Account Budget crossovered report"
    _columns = {
        'date_from': fields.date('Start of period', required=True),
        'date_to': fields.date('End of period', required=True),
    }
    _defaults = {
        'date_from': lambda *a: time.strftime('%Y-01-01'),
        'date_to': lambda *a: time.strftime('%Y-%m-%d'),
    }

    def check_report(self, cr, uid, ids, context=None):
        if context is None:
            context = {}
        data = self.read(cr, uid, ids, context=context)[0]
        datas = {
            'ids': context.get('active_ids', []),
            'model': 'crossovered.budget',
            'form': data
        }
        datas['form']['ids'] = datas['ids']
        datas['form']['report'] = 'analytic-full'
        return self.pool['report'].get_action(
            cr,
            uid, [],
            'account_budget.report_crossoveredbudget',
            data=datas,
            context=context)
Ejemplo n.º 5
0
class hr_salary_employee_bymonth(osv.osv_memory):

    _name = 'hr.salary.employee.month'
    _description = 'Hr Salary Employee By Month Report'
    _columns = {
        'start_date':
        fields.date('Start Date', required=True),
        'end_date':
        fields.date('End Date', required=True),
        'employee_ids':
        fields.many2many('hr.employee',
                         'payroll_year_rel',
                         'payroll_year_id',
                         'employee_id',
                         'Employees',
                         required=True),
        'category_id':
        fields.many2one('hr.salary.rule.category', 'Category', required=True),
    }

    def _get_default_category(self, cr, uid, context=None):
        category_ids = self.pool.get('hr.salary.rule.category').search(
            cr, uid, [('code', '=', 'NET')], context=context)
        return category_ids and category_ids[0] or False

    _defaults = {
        'start_date': lambda *a: time.strftime('%Y-01-01'),
        'end_date': lambda *a: time.strftime('%Y-%m-%d'),
        'category_id': _get_default_category
    }

    def print_report(self, cr, uid, ids, context=None):
        """
         To get the date and print the report
         @param self: The object pointer.
         @param cr: A database cursor
         @param uid: ID of the user currently logged in
         @param context: A standard dictionary
         @return: return report
        """
        if context is None:
            context = {}
        datas = {'ids': context.get('active_ids', [])}

        res = self.read(cr, uid, ids, context=context)
        res = res and res[0] or {}
        datas.update({'form': res})
        return self.pool['report'].get_action(
            cr,
            uid,
            ids,
            'l10n_in_hr_payroll.report_hrsalarybymonth',
            data=datas,
            context=context)
Ejemplo n.º 6
0
class hr_attendance_error(osv.osv_memory):

    _name = 'hr.attendance.error'
    _description = 'Print Error Attendance Report'
    _columns = {
        'init_date':
        fields.date('Starting Date', required=True),
        'end_date':
        fields.date('Ending Date', required=True),
        'max_delay':
        fields.integer(
            'Maximum Tolerance (in minutes)',
            required=True,
            help=
            "Allowed difference in minutes between the signin/signout and the timesheet computation for one sheet. Set this to 0 for no tolerance."
        )
    }
    _defaults = {
        'init_date': lambda *a: time.strftime('%Y-%m-%d'),
        'end_date': lambda *a: time.strftime('%Y-%m-%d'),
        'max_delay': 120,
    }

    def print_report(self, cr, uid, ids, context=None):
        emp_ids = []
        data_error = self.read(cr, uid, ids, context=context)[0]
        date_from = data_error['init_date']
        date_to = data_error['end_date']
        cr.execute(
            "SELECT id FROM hr_attendance WHERE employee_id IN %s AND to_char(name,'YYYY-mm-dd')<=%s AND to_char(name,'YYYY-mm-dd')>=%s AND action IN %s ORDER BY name",
            (tuple(context['active_ids']), date_to, date_from,
             tuple(['sign_in', 'sign_out'])))
        attendance_ids = [x[0] for x in cr.fetchall()]
        if not attendance_ids:
            raise UserError(_('No records are found for your selection!'))
        attendance_records = self.pool.get('hr.attendance').browse(
            cr, uid, attendance_ids, context=context)

        for rec in attendance_records:
            if rec.employee_id.id not in emp_ids:
                emp_ids.append(rec.employee_id.id)
        data_error['emp_ids'] = emp_ids
        datas = {'ids': [], 'model': 'hr.employee', 'form': data_error}
        return self.pool['report'].get_action(
            cr,
            uid, [],
            'hr_attendance.report_attendanceerrors',
            data=datas,
            context=context)
Ejemplo n.º 7
0
class purchase_requisition_line(osv.osv):
    _name = "purchase.requisition.line"
    _description = "Purchase Requisition Line"
    _rec_name = 'product_id'

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

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

    _defaults = {
        'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'purchase.requisition.line', context=c),
    }
Ejemplo n.º 8
0
class report_sales_by_user_pos_month(osv.osv):
    _name = "report.sales.by.user.pos.month"
    _description = "Sales by user monthly"
    _auto = False
    _columns = {
        'date_order': fields.date('Order Date',required=True, select=True),
        'amount': fields.float('Total', readonly=True, select=True),
        'qty': fields.float('Quantity', readonly=True, select=True),
        'user_id': fields.many2one('res.users', 'User', readonly=True, select=True),
    }

    def init(self, cr):
        tools.drop_view_if_exists(cr, 'report_sales_by_user_pos_month')
        cr.execute("""
            create or replace view report_sales_by_user_pos_month as (
                select
                    min(po.id) as id,
                    to_char(date_trunc('month',po.date_order),'YYYY-MM-DD')::text as date_order,
                    po.user_id as user_id,
                    sum(pol.qty)as qty,
                    sum((pol.price_unit * pol.qty * (1 - (pol.discount) / 100.0))) as amount
                from
                    pos_order as po,pos_order_line as pol,product_product as pp,product_template as pt
                where
                    pt.id=pp.product_tmpl_id and pp.id=pol.product_id and po.id = pol.order_id
               group by
                    to_char(date_trunc('month',po.date_order),'YYYY-MM-DD')::text,
                    po.user_id

                )
        """)
Ejemplo n.º 9
0
class hr_holidays_summary_dept(osv.osv_memory):
    _name = 'hr.holidays.summary.dept'
    _description = 'HR Leaves Summary Report By Department'
    _columns = {
        'date_from':
        fields.date('From', required=True),
        'depts':
        fields.many2many('hr.department', 'summary_dept_rel', 'sum_id',
                         'dept_id', 'Department(s)'),
        'holiday_type':
        fields.selection([('Approved', 'Approved'), ('Confirmed', 'Confirmed'),
                          ('both', 'Both Approved and Confirmed')],
                         'Leave Type',
                         required=True)
    }

    _defaults = {
        'date_from': lambda *a: time.strftime('%Y-%m-01'),
        'holiday_type': 'Approved'
    }

    def print_report(self, cr, uid, ids, context=None):
        data = self.read(cr, uid, ids, context=context)[0]
        if not data['depts']:
            raise UserError(
                _('You have to select at least one Department. And try again.')
            )
        datas = {'ids': [], 'model': 'hr.department', 'form': data}
        return self.pool['report'].get_action(
            cr,
            uid,
            data['depts'],
            'hr_holidays.report_holidayssummary',
            data=datas,
            context=context)
Ejemplo n.º 10
0
class res_partner(osv.osv):
    _inherit = "res.partner"

    _columns = {
        'partner_latitude': fields.float('Geo Latitude', digits=(16, 5)),
        'partner_longitude': fields.float('Geo Longitude', digits=(16, 5)),
        'date_localization': fields.date('Geo Localization Date'),
    }

    def geo_localize(self, cr, uid, ids, context=None):
        # Don't pass context to browse()! We need country names in english below
        for partner in self.browse(cr, uid, ids):
            if not partner:
                continue
            result = geo_find(geo_query_address(street=partner.street,
                                                zip=partner.zip,
                                                city=partner.city,
                                                state=partner.state_id.name,
                                                country=partner.country_id.name))
            if result:
                self.write(cr, uid, [partner.id], {
                    'partner_latitude': result[0],
                    'partner_longitude': result[1],
                    'date_localization': fields.date.context_today(self, cr, uid, context=context)
                }, context=context)
        return True
Ejemplo n.º 11
0
class hr_holidays_summary_employee(osv.osv_memory):
    _name = 'hr.holidays.summary.employee'
    _description = 'HR Leaves Summary Report By Employee'
    _columns = {
        'date_from':
        fields.date('From', required=True),
        'emp':
        fields.many2many('hr.employee', 'summary_emp_rel', 'sum_id', 'emp_id',
                         'Employee(s)'),
        'holiday_type':
        fields.selection([('Approved', 'Approved'), ('Confirmed', 'Confirmed'),
                          ('both', 'Both Approved and Confirmed')],
                         'Select Leave Type',
                         required=True)
    }

    _defaults = {
        'date_from': lambda *a: time.strftime('%Y-%m-01'),
        'holiday_type': 'Approved',
    }

    def print_report(self, cr, uid, ids, context=None):
        data = self.read(cr, uid, ids, context=context)[0]
        data['emp'] = context.get('active_ids', [])
        datas = {'ids': [], 'model': 'hr.employee', 'form': data}
        return self.pool['report'].get_action(
            cr,
            uid,
            data['emp'],
            'hr_holidays.report_holidayssummary',
            data=datas,
            context=context)
Ejemplo n.º 12
0
class account_analytic_default(osv.osv):
    _name = "account.analytic.default"
    _description = "Analytic Distribution"
    _rec_name = "analytic_id"
    _order = "sequence"
    _columns = {
        'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of analytic distribution"),
        'analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'),
        'product_id': fields.many2one('product.product', 'Product', ondelete='cascade', help="Select a product which will use analytic account specified in analytic default (e.g. create new customer invoice or Sales order if we select this product, it will automatically take this as an analytic account)"),
        'partner_id': fields.many2one('res.partner', 'Partner', ondelete='cascade', help="Select a partner which will use analytic account specified in analytic default (e.g. create new customer invoice or Sales order if we select this partner, it will automatically take this as an analytic account)"),
        'user_id': fields.many2one('res.users', 'User', ondelete='cascade', help="Select a user which will use analytic account specified in analytic default."),
        'company_id': fields.many2one('res.company', 'Company', ondelete='cascade', help="Select a company which will use analytic account specified in analytic default (e.g. create new customer invoice or Sales order if we select this company, it will automatically take this as an analytic account)"),
        'date_start': fields.date('Start Date', help="Default start date for this Analytic Account."),
        'date_stop': fields.date('End Date', help="Default end date for this Analytic Account."),
    }

    def account_get(self, cr, uid, product_id=None, partner_id=None, user_id=None, date=None, company_id=None, context=None):
        domain = []
        if product_id:
            domain += ['|', ('product_id', '=', product_id)]
        domain += [('product_id','=', False)]
        if partner_id:
            domain += ['|', ('partner_id', '=', partner_id)]
        domain += [('partner_id', '=', False)]
        if company_id:
            domain += ['|', ('company_id', '=', company_id)]
        domain += [('company_id', '=', False)]
        if user_id:
            domain += ['|',('user_id', '=', user_id)]
        domain += [('user_id','=', False)]
        if date:
            domain += ['|', ('date_start', '<=', date), ('date_start', '=', False)]
            domain += ['|', ('date_stop', '>=', date), ('date_stop', '=', False)]
        best_index = -1
        res = False
        for rec in self.browse(cr, uid, self.search(cr, uid, domain, context=context), context=context):
            index = 0
            if rec.product_id: index += 1
            if rec.partner_id: index += 1
            if rec.company_id: index += 1
            if rec.user_id: index += 1
            if rec.date_start: index += 1
            if rec.date_stop: index += 1
            if index > best_index:
                res = rec
                best_index = index
        return res
Ejemplo n.º 13
0
class yearly_salary_detail(osv.osv_memory):

    _name = 'yearly.salary.detail'
    _description = 'Hr Salary Employee By Category Report'
    _columns = {
        'employee_ids':
        fields.many2many('hr.employee',
                         'payroll_emp_rel',
                         'payroll_id',
                         'employee_id',
                         'Employees',
                         required=True),
        'date_from':
        fields.date('Start Date', required=True),
        'date_to':
        fields.date('End Date', required=True),
    }

    _defaults = {
        'date_from': lambda *a: time.strftime('%Y-01-01'),
        'date_to': lambda *a: time.strftime('%Y-%m-%d'),
    }

    def print_report(self, cr, uid, ids, context=None):
        """
         To get the date and print the report
         @param self: The object pointer.
         @param cr: A database cursor
         @param uid: ID of the user currently logged in
         @param context: A standard dictionary
         @return: return report
        """
        if context is None:
            context = {}
        datas = {'ids': context.get('active_ids', [])}

        res = self.read(cr, uid, ids, context=context)
        res = res and res[0] or {}
        datas.update({'form': res})
        return self.pool['report'].get_action(
            cr,
            uid,
            ids,
            'l10n_in_hr_payroll.report_hryearlysalary',
            data=datas,
            context=context)
Ejemplo n.º 14
0
class pos_details(osv.osv_memory):
    _name = 'pos.details'
    _description = 'Sales Details'

    _columns = {
        'date_start':
        fields.date('Date Start', required=True),
        'date_end':
        fields.date('Date End', required=True),
        'user_ids':
        fields.many2many('res.users', 'pos_details_report_user_rel', 'user_id',
                         'wizard_id', 'Salespeople'),
    }
    _defaults = {
        'date_start': fields.date.context_today,
        'date_end': fields.date.context_today,
    }

    def print_report(self, cr, uid, ids, context=None):
        """
         To get the date and print the report
         @param self: The object pointer.
         @param cr: A database cursor
         @param uid: ID of the user currently logged in
         @param context: A standard dictionary
         @return : retrun report
        """
        if context is None:
            context = {}
        datas = {'ids': context.get('active_ids', [])}
        res = self.read(cr,
                        uid,
                        ids, ['date_start', 'date_end', 'user_ids'],
                        context=context)
        res = res and res[0] or {}
        datas['form'] = res
        if res.get('id', False):
            datas['ids'] = [res['id']]
        return self.pool['report'].get_action(
            cr,
            uid, [],
            'point_of_sale.report_detailsofsales',
            data=datas,
            context=context)
Ejemplo n.º 15
0
class crm_partner_report_assign(osv.osv):
    """ CRM Lead Report """
    _name = "crm.partner.report.assign"
    _auto = False
    _description = "CRM Partner Report"
    _columns = {
        'partner_id': fields.many2one('res.partner', 'Partner', required=False, readonly=True),
        'grade_id':fields.many2one('res.partner.grade', 'Grade', readonly=True),
        'activation' : fields.many2one('res.partner.activation', 'Activation', select=1),
        'user_id':fields.many2one('res.users', 'User', readonly=True),
        'date_review' : fields.date('Latest Partner Review'),
        'date_partnership' : fields.date('Partnership Date'),
        'country_id':fields.many2one('res.country', 'Country', readonly=True),
        'team_id':fields.many2one('crm.team', 'Sales Team', oldname='section_id', readonly=True),
        'opp': fields.integer('# of Opportunity', readonly=True),  # TDE FIXME master: rename into nbr_opportunities
        'turnover': fields.float('Turnover', readonly=True),
        'date': fields.date('Invoice Account Date', readonly=True),
    }
    def init(self, cr):
        """
            CRM Lead Report
            @param cr: the current row, from the database cursor
        """
        tools.drop_view_if_exists(cr, 'crm_partner_report_assign')
        cr.execute("""
            CREATE OR REPLACE VIEW crm_partner_report_assign AS (
                SELECT
                    coalesce(i.id, p.id - 1000000000) as id,
                    p.id as partner_id,
                    (SELECT country_id FROM res_partner a WHERE a.parent_id=p.id AND country_id is not null limit 1) as country_id,
                    p.grade_id,
                    p.activation,
                    p.date_review,
                    p.date_partnership,
                    p.user_id,
                    p.team_id,
                    (SELECT count(id) FROM crm_lead WHERE partner_assigned_id=p.id) AS opp,
                    i.price_total as turnover,
                    i.date
                FROM
                    res_partner p
                    left join account_invoice_report i
                        on (i.partner_id=p.id and i.type in ('out_invoice','out_refund') and i.state in ('open','paid'))
            )""")
Ejemplo n.º 16
0
class res_partner(osv.osv):
    _inherit = "res.partner"
    _columns = {
        'partner_weight':
        fields.integer(
            'Level Weight',
            help=
            "Gives the probability to assign a lead to this partner. (0 means no assignation.)"
        ),
        'grade_id':
        fields.many2one('res.partner.grade', 'Level'),
        'activation':
        fields.many2one('res.partner.activation', 'Activation', select=1),
        'date_partnership':
        fields.date('Partnership Date'),
        'date_review':
        fields.date('Latest Partner Review'),
        'date_review_next':
        fields.date('Next Partner Review'),
        # customer implementation
        'assigned_partner_id':
        fields.many2one(
            'res.partner',
            'Implemented by',
        ),
        'implemented_partner_ids':
        fields.one2many(
            'res.partner',
            'assigned_partner_id',
            string='Implementation References',
        ),
    }
    _defaults = {'partner_weight': lambda *args: 0}

    def onchange_grade_id(self, cr, uid, ids, grade_id, context=None):
        res = {'value': {'partner_weight': 0}}
        if grade_id:
            partner_grade = self.pool.get('res.partner.grade').browse(
                cr, uid, grade_id)
            res['value']['partner_weight'] = partner_grade.partner_weight
        return res
class payslip_lines_contribution_register(osv.osv_memory):
    _name = 'payslip.lines.contribution.register'
    _description = 'PaySlip Lines by Contribution Registers'
    _columns = {
        'date_from': fields.date('Date From', required=True),
        'date_to': fields.date('Date To', required=True),
    }

    _defaults = {
        'date_from': lambda *a: time.strftime('%Y-%m-01'),
        'date_to': lambda *a: str(datetime.now() + relativedelta.relativedelta(months=+1, day=1, days=-1))[:10],
    }

    def print_report(self, cr, uid, ids, context=None):
        datas = {
             'ids': context.get('active_ids', []),
             'model': 'hr.contribution.register',
             'form': self.read(cr, uid, ids, context=context)[0]
        }
        return self.pool['report'].get_action(
            cr, uid, [], 'hr_payroll.report_contributionregister', data=datas, context=context
        )
Ejemplo n.º 18
0
class test_converter(orm.Model):
    _name = 'web_editor.converter.test'

    # disable translation export for those brilliant field labels and values
    _translate = False

    _columns = {
        'char':
        fields.char(),
        'integer':
        fields.integer(),
        'float':
        fields.float(),
        'numeric':
        fields.float(digits=(16, 2)),
        'many2one':
        fields.many2one('web_editor.converter.test.sub'),
        'binary':
        fields.binary(),
        'date':
        fields.date(),
        'datetime':
        fields.datetime(),
        'selection':
        fields.selection([
            (1, "réponse A"),
            (2, "réponse B"),
            (3, "réponse C"),
            (4, "réponse D"),
        ]),
        'selection_str':
        fields.selection(
            [
                ('A', "Qu'il n'est pas arrivé à Toronto"),
                ('B', "Qu'il était supposé arriver à Toronto"),
                ('C', "Qu'est-ce qu'il fout ce maudit pancake, tabernacle ?"),
                ('D', "La réponse D"),
            ],
            string=
            u"Lorsqu'un pancake prend l'avion à destination de Toronto et "
            u"qu'il fait une escale technique à St Claude, on dit:"),
        'html':
        fields.html(),
        'text':
        fields.text(),
    }
Ejemplo n.º 19
0
class stock_inventory(osv.osv):
    _inherit = "stock.inventory"
    _columns = {
        'accounting_date':
        fields.date(
            'Force Accounting Date',
            help=
            "Choose the accounting date at which you want to value the stock moves created by the inventory instead of the default one (the inventory end date)"
        ),
    }

    def post_inventory(self, cr, uid, inv, context=None):
        if context is None:
            context = {}
        ctx = context.copy()
        if inv.accounting_date:
            ctx['force_period_date'] = inv.accounting_date
        return super(stock_inventory, self).post_inventory(cr,
                                                           uid,
                                                           inv,
                                                           context=ctx)
Ejemplo n.º 20
0
class product_pricelist_item(osv.osv):
    _name = "product.pricelist.item"
    _description = "Pricelist item"
    _order = "applied_on, min_quantity desc"

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

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

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

    _defaults = {
        'base': 'list_price',
        'min_quantity': 1,
        'sequence': 5,
        'price_discount': 0,
        'applied_on': '3_global',
    }
    _constraints = [
        (_check_recursion,
         'Error! You cannot assign the Main Pricelist as Other Pricelist in PriceList Item!',
         ['base_pricelist_id']),
        (_check_margin,
         'Error! The minimum margin should be lower than the maximum margin.',
         ['price_min_margin', 'price_max_margin'])
    ]
Ejemplo n.º 21
0
class make_procurement(osv.osv_memory):
    _name = 'make.procurement'
    _description = 'Make Procurements'
    
    def onchange_product_id(self, cr, uid, ids, prod_id, context=None):
        product = self.pool.get('product.product').browse(cr, uid, prod_id, context=context)
        return {'value': {
            'uom_id': product.uom_id.id,
            'product_tmpl_id': product.product_tmpl_id.id,
            'product_variant_count': product.product_tmpl_id.product_variant_count
        }}
    
    _columns = {
        'qty': fields.float('Quantity', digits=(16,2), required=True),
        'res_model': fields.char('Res Model'),
        'product_id': fields.many2one('product.product', 'Product', required=True),
        'product_tmpl_id': fields.many2one('product.template', 'Template', required=True),
        'product_variant_count': fields.related('product_tmpl_id', 'product_variant_count', type='integer', string='Variant Number'),
        'uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True),
        'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', required=True),
        'date_planned': fields.date('Planned Date', required=True),
        'route_ids': fields.many2many('stock.location.route', string='Preferred Routes'),
    }

    _defaults = {
        'date_planned': fields.date.context_today,
        'qty': lambda *args: 1.0,
    }

    def make_procurement(self, cr, uid, ids, context=None):
        """ Creates procurement order for selected product. """
        user = self.pool.get('res.users').browse(cr, uid, uid, context=context).login
        wh_obj = self.pool.get('stock.warehouse')
        procurement_obj = self.pool.get('procurement.order')
        data_obj = self.pool.get('ir.model.data')

        for proc in self.browse(cr, uid, ids, context=context):
            wh = wh_obj.browse(cr, uid, proc.warehouse_id.id, context=context)
            procure_id = procurement_obj.create(cr, uid, {
                'name':'INT: '+str(user),
                'date_planned': proc.date_planned,
                'product_id': proc.product_id.id,
                'product_qty': proc.qty,
                'product_uom': proc.uom_id.id,
                'warehouse_id': proc.warehouse_id.id,
                'location_id': wh.lot_stock_id.id,
                'company_id': wh.company_id.id,
                'route_ids': [(6, 0, proc.route_ids.ids)],
            })
            procurement_obj.signal_workflow(cr, uid, [procure_id], 'button_confirm')

        id2 = data_obj._get_id(cr, uid, 'procurement', 'procurement_tree_view')
        id3 = data_obj._get_id(cr, uid, 'procurement', 'procurement_form_view')

        if id2:
            id2 = data_obj.browse(cr, uid, id2, context=context).res_id
        if id3:
            id3 = data_obj.browse(cr, uid, id3, context=context).res_id

        return {
            'view_type': 'form',
            'view_mode': 'tree,form',
            'res_model': 'procurement.order',
            'res_id' : procure_id,
            'views': [(id3,'form'),(id2,'tree')],
            'type': 'ir.actions.act_window',
         }

    def default_get(self, cr, uid, fields, context=None):
        if context is None:
            context = {}
        record_id = context.get('active_id')

        if context.get('active_model') == 'product.template':
            product_ids = self.pool.get('product.product').search(cr, uid, [('product_tmpl_id', '=', context.get('active_id'))], context=context)
            if product_ids:
                record_id = product_ids[0]

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

        if record_id and 'product_id' in fields:
            proxy = self.pool.get('product.product')
            product_ids = proxy.search(cr, uid, [('id', '=', record_id)], context=context, limit=1)
            if product_ids:
                product_id = product_ids[0]

                product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
                res['product_id'] = product.id
                res['uom_id'] = product.uom_id.id

        if 'warehouse_id' in fields:
            warehouse_id = self.pool.get('stock.warehouse').search(cr, uid, [], context=context)
            res['warehouse_id'] = warehouse_id[0] if warehouse_id else False

        return res

    def create(self, cr, uid, values, context=None):
        if values.get('product_id'):
            values.update(self.onchange_product_id(cr, uid, None, values['product_id'], context=context)['value'])
        return super(make_procurement, self).create(cr, uid, values, context=context)
Ejemplo n.º 22
0
class mrp_repair(osv.osv):
    _name = 'mrp.repair'
    _inherit = 'mail.thread'
    _description = 'Repair Order'

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def action_repair_done(self, cr, uid, ids, context=None):
        """ Creates stock move for operation and stock move for final product of repair order.
        @return: Move ids of final products
        """
        res = {}
        move_obj = self.pool.get('stock.move')
        repair_line_obj = self.pool.get('mrp.repair.line')
        for repair in self.browse(cr, uid, ids, context=context):
            move_ids = []
            for move in repair.operations:
                move_id = move_obj.create(cr, uid, {
                    'name': move.name,
                    'product_id': move.product_id.id,
                    'restrict_lot_id': move.lot_id.id,
                    'product_uom_qty': move.product_uom_qty,
                    'product_uom': move.product_uom.id,
                    'partner_id': repair.address_id and repair.address_id.id or False,
                    'location_id': move.location_id.id,
                    'location_dest_id': move.location_dest_id.id,
                })
                move_ids.append(move_id)
                repair_line_obj.write(cr, uid, [move.id], {'move_id': move_id, 'state': 'done'}, context=context)
            move_id = move_obj.create(cr, uid, {
                'name': repair.name,
                'product_id': repair.product_id.id,
                'product_uom': repair.product_uom.id or repair.product_id.uom_id.id,
                'product_uom_qty': repair.product_qty,
                'partner_id': repair.address_id and repair.address_id.id or False,
                'location_id': repair.location_id.id,
                'location_dest_id': repair.location_dest_id.id,
                'restrict_lot_id': repair.lot_id.id,
            })
            move_ids.append(move_id)
            move_obj.action_done(cr, uid, move_ids, context=context)
            self.write(cr, uid, [repair.id], {'state': 'done', 'move_id': move_id}, context=context)
            res[repair.id] = move_id
        return res
Ejemplo n.º 23
0
class payroll_advice(osv.osv):
    '''
    Bank Advice
    '''
    _name = 'hr.payroll.advice'
    _description = 'Bank Advice'
    _columns = {
        'name':
        fields.char(
            'Name',
            readonly=True,
            required=True,
            states={'draft': [('readonly', False)]},
        ),
        'note':
        fields.text('Description'),
        'date':
        fields.date('Date',
                    readonly=True,
                    required=True,
                    states={'draft': [('readonly', False)]},
                    help="Advice Date is used to search Payslips"),
        'state':
        fields.selection([
            ('draft', 'Draft'),
            ('confirm', 'Confirmed'),
            ('cancel', 'Cancelled'),
        ],
                         'Status',
                         select=True,
                         readonly=True),
        'number':
        fields.char('Reference', readonly=True),
        'line_ids':
        fields.one2many('hr.payroll.advice.line',
                        'advice_id',
                        'Employee Salary',
                        states={'draft': [('readonly', False)]},
                        readonly=True,
                        copy=True),
        'chaque_nos':
        fields.char('Cheque Numbers'),
        'neft':
        fields.boolean(
            'NEFT Transaction',
            help="Check this box if your company use online transfer for salary"
        ),
        'company_id':
        fields.many2one('res.company',
                        'Company',
                        required=True,
                        readonly=True,
                        states={'draft': [('readonly', False)]}),
        'bank_id':
        fields.many2one(
            'res.bank',
            'Bank',
            readonly=True,
            states={'draft': [('readonly', False)]
                    },
            help="Select the Bank from which the salary is going to be paid"),
        'batch_id':
        fields.many2one('hr.payslip.run', 'Batch', readonly=True)
    }

    _defaults = {
        'date': lambda * a: time.strftime('%Y-%m-%d'),
        'state': lambda * a: 'draft',
        'company_id': lambda self, cr, uid, context: \
                self.pool.get('res.users').browse(cr, uid, uid,
                    context=context).company_id.id,
        'note': "Please make the payroll transfer from above account number to the below mentioned account numbers towards employee salaries:"
    }

    def compute_advice(self, cr, uid, ids, context=None):
        """
        Advice - Create Advice lines in Payment Advice and
        compute Advice lines.
        @param cr: the current row, from the database cursor,
        @param uid: the current user’s ID for security checks,
        @param ids: List of Advice’s IDs
        @return: Advice lines
        @param context: A standard dictionary for contextual values
        """
        payslip_pool = self.pool.get('hr.payslip')
        advice_line_pool = self.pool.get('hr.payroll.advice.line')
        payslip_line_pool = self.pool.get('hr.payslip.line')

        for advice in self.browse(cr, uid, ids, context=context):
            old_line_ids = advice_line_pool.search(
                cr, uid, [('advice_id', '=', advice.id)], context=context)
            if old_line_ids:
                advice_line_pool.unlink(cr, uid, old_line_ids, context=context)
            slip_ids = payslip_pool.search(cr,
                                           uid,
                                           [('date_from', '<=', advice.date),
                                            ('date_to', '>=', advice.date),
                                            ('state', '=', 'done')],
                                           context=context)
            for slip in payslip_pool.browse(cr, uid, slip_ids,
                                            context=context):
                if not slip.employee_id.bank_account_id and not slip.employee_id.bank_account_id.acc_number:
                    raise UserError(
                        _('Please define bank account for the %s employee') %
                        (slip.employee_id.name, ))
                line_ids = payslip_line_pool.search(cr,
                                                    uid,
                                                    [('slip_id', '=', slip.id),
                                                     ('code', '=', 'NET')],
                                                    context=context)
                if line_ids:
                    line = payslip_line_pool.browse(cr,
                                                    uid,
                                                    line_ids,
                                                    context=context)[0]
                    advice_line = {
                        'advice_id': advice.id,
                        'name': slip.employee_id.bank_account_id.acc_number,
                        'employee_id': slip.employee_id.id,
                        'bysal': line.total
                    }
                    advice_line_pool.create(cr,
                                            uid,
                                            advice_line,
                                            context=context)
                payslip_pool.write(cr,
                                   uid,
                                   slip_ids, {'advice_id': advice.id},
                                   context=context)
        return True

    def confirm_sheet(self, cr, uid, ids, context=None):
        """
        confirm Advice - confirmed Advice after computing Advice Lines..
        @param cr: the current row, from the database cursor,
        @param uid: the current user’s ID for security checks,
        @param ids: List of confirm Advice’s IDs
        @return: confirmed Advice lines and set sequence of Advice.
        @param context: A standard dictionary for contextual values
        """
        seq_obj = self.pool.get('ir.sequence')
        for advice in self.browse(cr, uid, ids, context=context):
            if not advice.line_ids:
                raise UserError(
                    _('You can not confirm Payment advice without advice lines.'
                      ))
            advice_date = datetime.strptime(advice.date, DATETIME_FORMAT)
            advice_year = advice_date.strftime(
                '%m') + '-' + advice_date.strftime('%Y')
            number = seq_obj.next_by_code(cr, uid, 'payment.advice')
            sequence_num = 'PAY' + '/' + advice_year + '/' + number
            self.write(cr,
                       uid, [advice.id], {
                           'number': sequence_num,
                           'state': 'confirm'
                       },
                       context=context)
        return True

    def set_to_draft(self, cr, uid, ids, context=None):
        """Resets Advice as draft.
        """
        return self.write(cr, uid, ids, {'state': 'draft'}, context=context)

    def cancel_sheet(self, cr, uid, ids, context=None):
        """Marks Advice as cancelled.
        """
        return self.write(cr, uid, ids, {'state': 'cancel'}, context=context)

    def onchange_company_id(self,
                            cr,
                            uid,
                            ids,
                            company_id=False,
                            context=None):
        res = {}
        if company_id:
            company = self.pool.get('res.company').browse(cr,
                                                          uid, [company_id],
                                                          context=context)[0]
            if company.partner_id.bank_ids:
                res.update(
                    {'bank_id': company.partner_id.bank_ids[0].bank_id.id})
        return {'value': res}
Ejemplo n.º 24
0
Archivo: note.py Proyecto: LiberTang0/5
class note_note(osv.osv):
    """ Note """
    _name = 'note.note'
    _inherit = ['mail.thread']
    _description = "Note"

    #writing method (no modification of values)
    def name_create(self, cr, uid, name, context=None):
        rec_id = self.create(cr, uid, {'memo': name}, context=context)
        return self.name_get(cr, uid, [rec_id], context)[0]

    #read the first line (convert hml into text)
    def _get_note_first_line(self,
                             cr,
                             uid,
                             ids,
                             name="",
                             args={},
                             context=None):
        res = {}
        for note in self.browse(cr, uid, ids, context=context):
            res[note.id] = (note.memo and html2plaintext(note.memo)
                            or "").strip().replace('*', '').split("\n")[0]

        return res

    def onclick_note_is_done(self, cr, uid, ids, context=None):
        return self.write(cr,
                          uid,
                          ids, {
                              'open': False,
                              'date_done': fields.date.today()
                          },
                          context=context)

    def onclick_note_not_done(self, cr, uid, ids, context=None):
        return self.write(cr, uid, ids, {'open': True}, context=context)

    #return the default stage for the uid user
    def _get_default_stage_id(self, cr, uid, context=None):
        ids = self.pool.get('note.stage').search(cr,
                                                 uid, [('user_id', '=', uid)],
                                                 context=context)
        return ids and ids[0] or False

    def _set_stage_per_user(self,
                            cr,
                            uid,
                            id,
                            name,
                            value,
                            args=None,
                            context=None):
        note = self.browse(cr, uid, id, context=context)
        if not value: return False
        stage_ids = [value] + [
            stage.id for stage in note.stage_ids if stage.user_id.id != uid
        ]
        return self.write(cr,
                          uid, [id], {'stage_ids': [(6, 0, set(stage_ids))]},
                          context=context)

    def _get_stage_per_user(self, cr, uid, ids, name, args, context=None):
        result = dict.fromkeys(ids, False)
        for record in self.browse(cr, uid, ids, context=context):
            for stage in record.stage_ids:
                if stage.user_id.id == uid:
                    result[record.id] = stage.id
        return result

    _columns = {
        'name':
        fields.function(_get_note_first_line,
                        string='Note Summary',
                        type='text',
                        store=True),
        'user_id':
        fields.many2one('res.users', 'Owner'),
        'memo':
        fields.html('Note Content'),
        'sequence':
        fields.integer('Sequence'),
        'stage_id':
        fields.function(_get_stage_per_user,
                        fnct_inv=_set_stage_per_user,
                        string='Stage',
                        type='many2one',
                        relation='note.stage'),
        'stage_ids':
        fields.many2many('note.stage', 'note_stage_rel', 'note_id', 'stage_id',
                         'Stages of Users'),
        'open':
        fields.boolean('Active', track_visibility='onchange'),
        'date_done':
        fields.date('Date done'),
        'color':
        fields.integer('Color Index'),
        'tag_ids':
        fields.many2many('note.tag', 'note_tags_rel', 'note_id', 'tag_id',
                         'Tags'),
    }
    _defaults = {
        'user_id': lambda self, cr, uid, ctx=None: uid,
        'open': 1,
        'stage_id': _get_default_stage_id,
    }
    _order = 'sequence'

    def read_group(self,
                   cr,
                   uid,
                   domain,
                   fields,
                   groupby,
                   offset=0,
                   limit=None,
                   context=None,
                   orderby=False,
                   lazy=True):
        if groupby and groupby[0] == "stage_id":

            #search all stages
            current_stage_ids = self.pool.get('note.stage').search(
                cr, uid, [('user_id', '=', uid)], context=context)

            if current_stage_ids:  #if the user have some stages
                stages = self.pool['note.stage'].browse(cr,
                                                        uid,
                                                        current_stage_ids,
                                                        context=context)

                result = [{ #notes by stage for stages user
                        '__context': {'group_by': groupby[1:]},
                        '__domain': domain + [('stage_ids.id', '=', stage.id)],
                        'stage_id': (stage.id, stage.name),
                        'stage_id_count': self.search(cr,uid, domain+[('stage_ids', '=', stage.id)], context=context, count=True),
                        '__fold': stage.fold,
                    } for stage in stages]

                #note without user's stage
                nb_notes_ws = self.search(
                    cr,
                    uid,
                    domain + [('stage_ids', 'not in', current_stage_ids)],
                    context=context,
                    count=True)
                if nb_notes_ws:
                    # add note to the first column if it's the first stage
                    dom_not_in = ('stage_ids', 'not in', current_stage_ids)
                    if result and result[0]['stage_id'][
                            0] == current_stage_ids[0]:
                        dom_in = result[0]['__domain'].pop()
                        result[0]['__domain'] = domain + [
                            '|', dom_in, dom_not_in
                        ]
                        result[0]['stage_id_count'] += nb_notes_ws
                    else:
                        # add the first stage column
                        result = [{
                            '__context': {
                                'group_by': groupby[1:]
                            },
                            '__domain': domain + [dom_not_in],
                            'stage_id': (stages[0].id, stages[0].name),
                            'stage_id_count': nb_notes_ws,
                            '__fold': stages[0].name,
                        }] + result

            else:  # if stage_ids is empty

                #note without user's stage
                nb_notes_ws = self.search(cr,
                                          uid,
                                          domain,
                                          context=context,
                                          count=True)
                if nb_notes_ws:
                    result = [{  #notes for unknown stage
                        '__context': {
                            'group_by': groupby[1:]
                        },
                        '__domain': domain,
                        'stage_id': False,
                        'stage_id_count': nb_notes_ws
                    }]
                else:
                    result = []
            return result

        else:
            return super(note_note, self).read_group(cr,
                                                     uid,
                                                     domain,
                                                     fields,
                                                     groupby,
                                                     offset=offset,
                                                     limit=limit,
                                                     context=context,
                                                     orderby=orderby,
                                                     lazy=lazy)

    def _notification_get_recipient_groups(self,
                                           cr,
                                           uid,
                                           ids,
                                           message,
                                           recipients,
                                           context=None):
        res = super(note_note,
                    self)._notification_get_recipient_groups(cr,
                                                             uid,
                                                             ids,
                                                             message,
                                                             recipients,
                                                             context=context)
        new_action_id = self.pool['ir.model.data'].xmlid_to_res_id(
            cr, uid, 'note.action_note_note')
        new_action = self._notification_link_helper(cr,
                                                    uid,
                                                    ids,
                                                    'new',
                                                    context=context,
                                                    action_id=new_action_id)
        res['user'] = {
            'actions': [{
                'url': new_action,
                'title': _('New Note')
            }]
        }
        return res
Ejemplo n.º 25
0
class crm_claim_report(osv.osv):
    """ CRM Claim Report"""

    _name = "crm.claim.report"
    _auto = False
    _description = "CRM Claim Report"

    _columns = {
        'user_id':
        fields.many2one('res.users', 'User', readonly=True),
        'team_id':
        fields.many2one('crm.team',
                        'Team',
                        oldname='section_id',
                        readonly=True),
        'nbr':
        fields.integer(
            '# of Claims',
            readonly=True),  # TDE FIXME master: rename into nbr_claims
        'company_id':
        fields.many2one('res.company', 'Company', readonly=True),
        'create_date':
        fields.datetime('Create Date', readonly=True, select=True),
        'claim_date':
        fields.datetime('Claim Date', readonly=True),
        'delay_close':
        fields.float('Delay to close',
                     digits=(16, 2),
                     readonly=True,
                     group_operator="avg",
                     help="Number of Days to close the case"),
        'stage_id':
        fields.many2one('crm.claim.stage',
                        'Stage',
                        readonly=True,
                        domain="[('team_ids','=',team_id)]"),
        'categ_id':
        fields.many2one('crm.claim.category', 'Category', readonly=True),
        'partner_id':
        fields.many2one('res.partner', 'Partner', readonly=True),
        'company_id':
        fields.many2one('res.company', 'Company', readonly=True),
        'priority':
        fields.selection(AVAILABLE_PRIORITIES, 'Priority'),
        'type_action':
        fields.selection([('correction', 'Corrective Action'),
                          ('prevention', 'Preventive Action')], 'Action Type'),
        'date_closed':
        fields.datetime('Close Date', readonly=True, select=True),
        'date_deadline':
        fields.date('Deadline', readonly=True, select=True),
        'delay_expected':
        fields.float('Overpassed Deadline',
                     digits=(16, 2),
                     readonly=True,
                     group_operator="avg"),
        'email':
        fields.integer('# Emails', size=128, readonly=True),
        'subject':
        fields.char('Claim Subject', readonly=True)
    }

    def init(self, cr):
        """ Display Number of cases And Team Name
        @param cr: the current row, from the database cursor,
         """

        tools.drop_view_if_exists(cr, 'crm_claim_report')
        cr.execute("""
            create or replace view crm_claim_report as (
                select
                    min(c.id) as id,
                    c.date as claim_date,
                    c.date_closed as date_closed,
                    c.date_deadline as date_deadline,
                    c.user_id,
                    c.stage_id,
                    c.team_id,
                    c.partner_id,
                    c.company_id,
                    c.categ_id,
                    c.name as subject,
                    count(*) as nbr,
                    c.priority as priority,
                    c.type_action as type_action,
                    c.create_date as create_date,
                    avg(extract('epoch' from (c.date_closed-c.create_date)))/(3600*24) as  delay_close,
                    (SELECT count(id) FROM mail_message WHERE model='crm.claim' AND res_id=c.id) AS email,
                    extract('epoch' from (c.date_deadline - c.date_closed))/(3600*24) as  delay_expected
                from
                    crm_claim c
                group by c.date,\
                        c.user_id,c.team_id, c.stage_id,\
                        c.categ_id,c.partner_id,c.company_id,c.create_date,
                        c.priority,c.type_action,c.date_deadline,c.date_closed,c.id
            )""")
Ejemplo n.º 26
0
class hr_timesheet_sheet_sheet_day(osv.osv):
    _name = "hr_timesheet_sheet.sheet.day"
    _description = "Timesheets by Period"
    _auto = False
    _order = 'name'
    _columns = {
        'name':
        fields.date('Date', readonly=True),
        'sheet_id':
        fields.many2one('hr_timesheet_sheet.sheet',
                        'Sheet',
                        readonly=True,
                        select="1"),
        'total_timesheet':
        fields.float('Total Timesheet', readonly=True),
        'total_attendance':
        fields.float('Attendance', readonly=True),
        'total_difference':
        fields.float('Difference', readonly=True),
    }
    _depends = {
        'account.analytic.line': ['date', 'unit_amount'],
        'hr.attendance': ['action', 'name', 'sheet_id'],
    }

    def init(self, cr):
        cr.execute("""create or replace view hr_timesheet_sheet_sheet_day as
            SELECT
                id,
                name,
                sheet_id,
                total_timesheet,
                total_attendance,
                cast(round(cast(total_attendance - total_timesheet as Numeric),2) as Double Precision) AS total_difference
            FROM
                ((
                    SELECT
                        MAX(id) as id,
                        name,
                        sheet_id,
                        timezone,
                        SUM(total_timesheet) as total_timesheet,
                        CASE WHEN SUM(orphan_attendances) != 0
                            THEN (SUM(total_attendance) +
                                CASE WHEN current_date <> name
                                    THEN 1440
                                    ELSE (EXTRACT(hour FROM current_time AT TIME ZONE 'UTC' AT TIME ZONE coalesce(timezone, 'UTC')) * 60) + EXTRACT(minute FROM current_time AT TIME ZONE 'UTC' AT TIME ZONE coalesce(timezone, 'UTC'))
                                END
                                )
                            ELSE SUM(total_attendance)
                        END /60  as total_attendance
                    FROM
                        ((
                            select
                                min(l.id) as id,
                                p.tz as timezone,
                                l.date::date as name,
                                s.id as sheet_id,
                                sum(l.unit_amount) as total_timesheet,
                                0 as orphan_attendances,
                                0.0 as total_attendance
                            from
                            	account_analytic_line l
                                LEFT JOIN hr_timesheet_sheet_sheet s ON s.id = l.sheet_id
                                JOIN hr_employee e ON s.employee_id = e.id
                                JOIN resource_resource r ON e.resource_id = r.id
                                LEFT JOIN res_users u ON r.user_id = u.id
                                LEFT JOIN res_partner p ON u.partner_id = p.id
                            group by l.date::date, s.id, timezone
                        ) union (
                            select
                                -min(a.id) as id,
                                p.tz as timezone,
                                (a.name AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))::date as name,
                                s.id as sheet_id,
                                0.0 as total_timesheet,
                                SUM(CASE WHEN a.action = 'sign_in' THEN -1 ELSE 1 END) as orphan_attendances,
                                SUM(((EXTRACT(hour FROM (a.name AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))) * 60) + EXTRACT(minute FROM (a.name AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC')))) * (CASE WHEN a.action = 'sign_in' THEN -1 ELSE 1 END)) as total_attendance
                            from
                                hr_attendance a
                                LEFT JOIN hr_timesheet_sheet_sheet s
                                ON s.id = a.sheet_id
                                JOIN hr_employee e
                                ON a.employee_id = e.id
                                JOIN resource_resource r
                                ON e.resource_id = r.id
                                LEFT JOIN res_users u
                                ON r.user_id = u.id
                                LEFT JOIN res_partner p
                                ON u.partner_id = p.id
                            WHERE action in ('sign_in', 'sign_out')
                            group by (a.name AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))::date, s.id, timezone
                        )) AS foo
                        GROUP BY name, sheet_id, timezone
                )) AS bar""")
Ejemplo n.º 27
0
class hr_timesheet_sheet(osv.osv):
    _name = "hr_timesheet_sheet.sheet"
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _table = 'hr_timesheet_sheet_sheet'
    _order = "id desc"
    _description = "Timesheet"

    def _total(self, cr, uid, ids, name, args, context=None):
        """ Compute the attendances, analytic lines timesheets and differences between them
            for all the days of a timesheet and the current day
        """
        res = dict.fromkeys(
            ids, {
                'total_attendance': 0.0,
                'total_timesheet': 0.0,
                'total_difference': 0.0,
            })

        cr.execute(
            """
            SELECT sheet_id as id,
                   sum(total_attendance) as total_attendance,
                   sum(total_timesheet) as total_timesheet,
                   sum(total_difference) as  total_difference
            FROM hr_timesheet_sheet_sheet_day
            WHERE sheet_id IN %s
            GROUP BY sheet_id
        """, (tuple(ids), ))

        res.update(dict((x.pop('id'), x) for x in cr.dictfetchall()))

        return res

    def check_employee_attendance_state(self, cr, uid, sheet_id, context=None):
        ids_signin = self.pool.get('hr.attendance').search(
            cr, uid, [('sheet_id', '=', sheet_id), ('action', '=', 'sign_in')])
        ids_signout = self.pool.get('hr.attendance').search(
            cr, uid, [('sheet_id', '=', sheet_id),
                      ('action', '=', 'sign_out')])

        if len(ids_signin) != len(ids_signout):
            raise UserError(
                _('The timesheet cannot be validated as it does not contain an equal number of sign ins and sign outs.'
                  ))
        return True

    def copy(self, cr, uid, ids, *args, **argv):
        raise UserError(_('You cannot duplicate a timesheet.'))

    def create(self, cr, uid, vals, context=None):
        if 'employee_id' in vals:
            if not self.pool.get('hr.employee').browse(
                    cr, uid, vals['employee_id'], context=context).user_id:
                raise UserError(
                    _('In order to create a timesheet for this employee, you must link him/her to a user.'
                      ))
        if vals.get('attendances_ids'):
            # If attendances, we sort them by date asc before writing them, to satisfy the alternance constraint
            vals['attendances_ids'] = self.sort_attendances(
                cr, uid, vals['attendances_ids'], context=context)
        return super(hr_timesheet_sheet, self).create(cr,
                                                      uid,
                                                      vals,
                                                      context=context)

    def write(self, cr, uid, ids, vals, context=None):
        if 'employee_id' in vals:
            new_user_id = self.pool.get('hr.employee').browse(
                cr, uid, vals['employee_id'],
                context=context).user_id.id or False
            if not new_user_id:
                raise UserError(
                    _('In order to create a timesheet for this employee, you must link him/her to a user.'
                      ))
            if not self._sheet_date(
                    cr, uid, ids, forced_user_id=new_user_id, context=context):
                raise UserError(
                    _('You cannot have 2 timesheets that overlap!\nYou should use the menu \'My Timesheet\' to avoid this problem.'
                      ))
            if not self.pool.get('hr.employee').browse(
                    cr, uid, vals['employee_id'], context=context).product_id:
                raise UserError(
                    _('In order to create a timesheet for this employee, you must link the employee to a product.'
                      ))
        if vals.get('attendances_ids'):
            # If attendances, we sort them by date asc before writing them, to satisfy the alternance constraint
            # In addition to the date order, deleting attendances are done before inserting attendances
            vals['attendances_ids'] = self.sort_attendances(
                cr, uid, vals['attendances_ids'], context=context)
        res = super(hr_timesheet_sheet, self).write(cr,
                                                    uid,
                                                    ids,
                                                    vals,
                                                    context=context)
        if vals.get('attendances_ids'):
            for timesheet in self.browse(cr, uid, ids):
                if not self.pool['hr.attendance']._altern_si_so(
                        cr, uid, [att.id
                                  for att in timesheet.attendances_ids]):
                    raise UserError(
                        _('Error ! Sign in (resp. Sign out) must follow Sign out (resp. Sign in)'
                          ))
        return res

    def sort_attendances(self, cr, uid, attendance_tuples, context=None):
        date_attendances = []
        for att_tuple in attendance_tuples:
            if att_tuple[0] in [0, 1, 4]:
                if att_tuple[0] in [0, 1]:
                    if att_tuple[2] and att_tuple[2].has_key('name'):
                        name = att_tuple[2]['name']
                    else:
                        name = self.pool['hr.attendance'].browse(
                            cr, uid, att_tuple[1]).name
                else:
                    name = self.pool['hr.attendance'].browse(
                        cr, uid, att_tuple[1]).name
                date_attendances.append((1, name, att_tuple))
            elif att_tuple[0] in [2, 3]:
                date_attendances.append((0, self.pool['hr.attendance'].browse(
                    cr, uid, att_tuple[1]).name, att_tuple))
            else:
                date_attendances.append((0, False, att_tuple))
        date_attendances.sort()
        return [att[2] for att in date_attendances]

    def button_confirm(self, cr, uid, ids, context=None):
        for sheet in self.browse(cr, uid, ids, context=context):
            if sheet.employee_id and sheet.employee_id.parent_id and sheet.employee_id.parent_id.user_id:
                self.message_subscribe_users(
                    cr,
                    uid, [sheet.id],
                    user_ids=[sheet.employee_id.parent_id.user_id.id],
                    context=context)
            self.check_employee_attendance_state(cr,
                                                 uid,
                                                 sheet.id,
                                                 context=context)
            di = sheet.user_id.company_id.timesheet_max_difference
            if (abs(sheet.total_difference) < di) or not di:
                sheet.signal_workflow('confirm')
            else:
                raise UserError(
                    _('Please verify that the total difference of the sheet is lower than %.2f.'
                      ) % (di, ))
        return True

    def attendance_action_change(self, cr, uid, ids, context=None):
        hr_employee = self.pool.get('hr.employee')
        employee_ids = []
        for sheet in self.browse(cr, uid, ids, context=context):
            if sheet.employee_id.id not in employee_ids:
                employee_ids.append(sheet.employee_id.id)
        return hr_employee.attendance_action_change(cr,
                                                    uid,
                                                    employee_ids,
                                                    context=context)

    def _count_attendances(self, cr, uid, ids, field_name, arg, context=None):
        res = dict.fromkeys(ids, 0)
        attendances_groups = self.pool['hr.attendance'].read_group(
            cr,
            uid, [('sheet_id', 'in', ids)], ['sheet_id'],
            'sheet_id',
            context=context)
        for attendances in attendances_groups:
            res[attendances['sheet_id'][0]] = attendances['sheet_id_count']
        return res

    _columns = {
        'name':
        fields.char('Note',
                    select=1,
                    states={
                        'confirm': [('readonly', True)],
                        'done': [('readonly', True)]
                    }),
        'employee_id':
        fields.many2one('hr.employee', 'Employee', required=True),
        'user_id':
        fields.related(
            'employee_id',
            'user_id',
            type="many2one",
            relation="res.users",
            store=True,
            string="User",
            required=False,
            readonly=True
        ),  #fields.many2one('res.users', 'User', required=True, select=1, states={'confirm':[('readonly', True)], 'done':[('readonly', True)]}),
        'date_from':
        fields.date('Date from',
                    required=True,
                    select=1,
                    readonly=True,
                    states={'new': [('readonly', False)]}),
        'date_to':
        fields.date('Date to',
                    required=True,
                    select=1,
                    readonly=True,
                    states={'new': [('readonly', False)]}),
        'timesheet_ids':
        fields.one2many('account.analytic.line',
                        'sheet_id',
                        'Timesheet lines',
                        readonly=True,
                        states={
                            'draft': [('readonly', False)],
                            'new': [('readonly', False)]
                        }),
        'attendances_ids':
        fields.one2many('hr.attendance', 'sheet_id', 'Attendances'),
        'state':
        fields.selection(
            [('new', 'New'), ('draft', 'Open'),
             ('confirm', 'Waiting Approval'), ('done', 'Approved')],
            'Status',
            select=True,
            required=True,
            readonly=True,
            track_visibility='onchange',
            help=
            ' * The \'Draft\' status is used when a user is encoding a new and unconfirmed timesheet. \
                \n* The \'Confirmed\' status is used for to confirm the timesheet by user. \
                \n* The \'Done\' status is used when users timesheet is accepted by his/her senior.'
        ),
        'state_attendance':
        fields.related('employee_id',
                       'state',
                       type='selection',
                       selection=[('absent', 'Absent'),
                                  ('present', 'Present')],
                       string='Current Status',
                       readonly=True),
        'total_attendance':
        fields.function(_total,
                        method=True,
                        string='Total Attendance',
                        multi="_total"),
        'total_timesheet':
        fields.function(_total,
                        method=True,
                        string='Total Timesheet',
                        multi="_total"),
        'total_difference':
        fields.function(_total,
                        method=True,
                        string='Difference',
                        multi="_total"),
        'period_ids':
        fields.one2many('hr_timesheet_sheet.sheet.day',
                        'sheet_id',
                        'Period',
                        readonly=True),
        'account_ids':
        fields.one2many('hr_timesheet_sheet.sheet.account',
                        'sheet_id',
                        'Analytic accounts',
                        readonly=True),
        'company_id':
        fields.many2one('res.company', 'Company'),
        'department_id':
        fields.many2one('hr.department', 'Department'),
        'attendance_count':
        fields.function(_count_attendances,
                        type='integer',
                        string="Attendances"),
    }

    def _default_date_from(self, cr, uid, context=None):
        user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
        r = user.company_id and user.company_id.timesheet_range or 'month'
        if r == 'month':
            return time.strftime('%Y-%m-01')
        elif r == 'week':
            return (datetime.today() +
                    relativedelta(weekday=0, days=-6)).strftime('%Y-%m-%d')
        elif r == 'year':
            return time.strftime('%Y-01-01')
        return fields.date.context_today(self, cr, uid, context)

    def _default_date_to(self, cr, uid, context=None):
        user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
        r = user.company_id and user.company_id.timesheet_range or 'month'
        if r == 'month':
            return (
                datetime.today() +
                relativedelta(months=+1, day=1, days=-1)).strftime('%Y-%m-%d')
        elif r == 'week':
            return (datetime.today() +
                    relativedelta(weekday=6)).strftime('%Y-%m-%d')
        elif r == 'year':
            return time.strftime('%Y-12-31')
        return fields.date.context_today(self, cr, uid, context)

    def _default_employee(self, cr, uid, context=None):
        emp_ids = self.pool.get('hr.employee').search(cr,
                                                      uid,
                                                      [('user_id', '=', uid)],
                                                      context=context)
        return emp_ids and emp_ids[0] or False

    _defaults = {
        'date_from':
        _default_date_from,
        'date_to':
        _default_date_to,
        'state':
        'new',
        'employee_id':
        _default_employee,
        'company_id':
        lambda self, cr, uid, c: self.pool.get('res.company').
        _company_default_get(cr, uid, 'hr_timesheet_sheet.sheet', context=c)
    }

    def _sheet_date(self, cr, uid, ids, forced_user_id=False, context=None):
        for sheet in self.browse(cr, uid, ids, context=context):
            new_user_id = forced_user_id or sheet.employee_id.user_id and sheet.employee_id.user_id.id
            if new_user_id:
                cr.execute(
                    'SELECT id \
                    FROM hr_timesheet_sheet_sheet \
                    WHERE (date_from <= %s and %s <= date_to) \
                        AND user_id=%s \
                        AND id <> %s',
                    (sheet.date_to, sheet.date_from, new_user_id, sheet.id))
                if cr.fetchall():
                    return False
        return True

    _constraints = [
        (_sheet_date,
         'You cannot have 2 timesheets that overlap!\nPlease use the menu \'My Current Timesheet\' to avoid this problem.',
         ['date_from', 'date_to']),
    ]

    def action_set_to_draft(self, cr, uid, ids, *args):
        self.write(cr, uid, ids, {'state': 'draft'})
        self.create_workflow(cr, uid, ids)
        return True

    def name_get(self, cr, uid, ids, context=None):
        if not ids:
            return []
        if isinstance(ids, (long, int)):
            ids = [ids]
        # week number according to ISO 8601 Calendar
        return [(r['id'], _('Week ')+str(datetime.strptime(r['date_from'], '%Y-%m-%d').isocalendar()[1])) \
                for r in self.read(cr, uid, ids, ['date_from'],
                    context=context, load='_classic_write')]

    def unlink(self, cr, uid, ids, context=None):
        sheets = self.read(cr,
                           uid,
                           ids, ['state', 'total_attendance'],
                           context=context)
        for sheet in sheets:
            if sheet['state'] in ('confirm', 'done'):
                raise UserError(
                    _('You cannot delete a timesheet which is already confirmed.'
                      ))
            elif sheet['total_attendance'] <> 0.00:
                raise UserError(
                    _('You cannot delete a timesheet which have attendance entries.'
                      ))

        toremove = []
        analytic_timesheet = self.pool.get('account.analytic.line')
        for sheet in self.browse(cr, uid, ids, context=context):
            for timesheet in sheet.timesheet_ids:
                toremove.append(timesheet.id)
        analytic_timesheet.unlink(cr, uid, toremove, context=context)

        return super(hr_timesheet_sheet, self).unlink(cr,
                                                      uid,
                                                      ids,
                                                      context=context)

    def onchange_employee_id(self, cr, uid, ids, employee_id, context=None):
        department_id = False
        user_id = False
        if employee_id:
            empl_id = self.pool.get('hr.employee').browse(cr,
                                                          uid,
                                                          employee_id,
                                                          context=context)
            department_id = empl_id.department_id.id
            user_id = empl_id.user_id.id
        return {
            'value': {
                'department_id': department_id,
                'user_id': user_id,
            }
        }

    # ------------------------------------------------
    # OpenChatter methods and notifications
    # ------------------------------------------------

    def _track_subtype(self, cr, uid, ids, init_values, context=None):
        record = self.browse(cr, uid, ids[0], context=context)
        if 'state' in init_values and record.state == 'confirm':
            return 'hr_timesheet_sheet.mt_timesheet_confirmed'
        elif 'state' in init_values and record.state == 'done':
            return 'hr_timesheet_sheet.mt_timesheet_approved'
        return super(hr_timesheet_sheet, self)._track_subtype(cr,
                                                              uid,
                                                              ids,
                                                              init_values,
                                                              context=context)

    def _needaction_domain_get(self, cr, uid, context=None):
        emp_obj = self.pool.get('hr.employee')
        empids = emp_obj.search(cr,
                                uid, [('parent_id.user_id', '=', uid)],
                                context=context)
        if not empids:
            return False
        dom = ['&', ('state', '=', 'confirm'), ('employee_id', 'in', empids)]
        return dom
Ejemplo n.º 28
0
class project_issue(osv.Model):
    _name = "project.issue"
    _description = "Project Issue"
    _order = "priority desc, create_date desc"
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _mail_post_access = 'read'

    def _get_default_partner(self, cr, uid, context=None):
        if context is None:
            context = {}
        if 'default_project_id' in context:
            project = self.pool.get('project.project').browse(
                cr, uid, context['default_project_id'], context=context)
            if project and project.partner_id:
                return project.partner_id.id
        return False

    def _get_default_stage_id(self, cr, uid, context=None):
        """ Gives default stage_id """
        if context is None:
            context = {}
        return self.stage_find(cr,
                               uid, [],
                               context.get('default_project_id'),
                               [('fold', '=', False)],
                               context=context)

    def _read_group_stage_ids(self,
                              cr,
                              uid,
                              ids,
                              domain,
                              read_group_order=None,
                              access_rights_uid=None,
                              context=None):
        if context is None:
            context = {}
        access_rights_uid = access_rights_uid or uid
        stage_obj = self.pool.get('project.task.type')
        order = stage_obj._order
        # lame hack to allow reverting search, should just work in the trivial case
        if read_group_order == 'stage_id desc':
            order = "%s desc" % order
        # retrieve team_id from the context, add them to already fetched columns (ids)
        if 'default_project_id' in context:
            search_domain = [
                '|', ('project_ids', '=', context['default_project_id']),
                ('id', 'in', ids)
            ]
        else:
            search_domain = [('id', 'in', ids)]
        # perform search
        stage_ids = stage_obj._search(cr,
                                      uid,
                                      search_domain,
                                      order=order,
                                      access_rights_uid=access_rights_uid,
                                      context=context)
        result = stage_obj.name_get(cr,
                                    access_rights_uid,
                                    stage_ids,
                                    context=context)
        # restore order of the search
        result.sort(
            lambda x, y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))

        fold = {}
        for stage in stage_obj.browse(cr,
                                      access_rights_uid,
                                      stage_ids,
                                      context=context):
            fold[stage.id] = stage.fold or False
        return result, fold

    def _compute_day(self, cr, uid, ids, fields, args, context=None):
        """
        @param cr: the current row, from the database cursor,
        @param uid: the current user’s ID for security checks,
        @param ids: List of Openday’s IDs
        @return: difference between current date and log date
        @param context: A standard dictionary for contextual values
        """
        Calendar = self.pool['resource.calendar']

        res = dict((res_id, {}) for res_id in ids)
        for issue in self.browse(cr, uid, ids, context=context):
            values = {
                'day_open': 0.0,
                'day_close': 0.0,
                'working_hours_open': 0.0,
                'working_hours_close': 0.0,
                'days_since_creation': 0.0,
                'inactivity_days': 0.0,
            }
            # if the working hours on the project are not defined, use default ones (8 -> 12 and 13 -> 17 * 5), represented by None
            calendar_id = None
            if issue.project_id and issue.project_id.resource_calendar_id:
                calendar_id = issue.project_id.resource_calendar_id.id

            dt_create_date = datetime.strptime(issue.create_date,
                                               DEFAULT_SERVER_DATETIME_FORMAT)

            if issue.date_open:
                dt_date_open = datetime.strptime(
                    issue.date_open, DEFAULT_SERVER_DATETIME_FORMAT)
                values['day_open'] = (dt_date_open - dt_create_date
                                      ).total_seconds() / (24.0 * 3600)
                values['working_hours_open'] = Calendar._interval_hours_get(
                    cr,
                    uid,
                    calendar_id,
                    dt_create_date,
                    dt_date_open,
                    timezone_from_uid=issue.user_id.id or uid,
                    exclude_leaves=False,
                    context=context)

            if issue.date_closed:
                dt_date_closed = datetime.strptime(
                    issue.date_closed, DEFAULT_SERVER_DATETIME_FORMAT)
                values['day_close'] = (dt_date_closed - dt_create_date
                                       ).total_seconds() / (24.0 * 3600)
                values['working_hours_close'] = Calendar._interval_hours_get(
                    cr,
                    uid,
                    calendar_id,
                    dt_create_date,
                    dt_date_closed,
                    timezone_from_uid=issue.user_id.id or uid,
                    exclude_leaves=False,
                    context=context)

            days_since_creation = datetime.today() - dt_create_date
            values['days_since_creation'] = days_since_creation.days
            if issue.date_action_last:
                inactive_days = datetime.today() - datetime.strptime(
                    issue.date_action_last, DEFAULT_SERVER_DATETIME_FORMAT)
            elif issue.date_last_stage_update:
                inactive_days = datetime.today() - datetime.strptime(
                    issue.date_last_stage_update,
                    DEFAULT_SERVER_DATETIME_FORMAT)
            else:
                inactive_days = datetime.today() - datetime.strptime(
                    issue.create_date, DEFAULT_SERVER_DATETIME_FORMAT)
            values['inactivity_days'] = inactive_days.days

            # filter only required values
            for field in fields:
                res[issue.id][field] = values[field]

        return res

    def on_change_project(self, cr, uid, ids, project_id, context=None):
        if project_id:
            project = self.pool.get('project.project').browse(cr,
                                                              uid,
                                                              project_id,
                                                              context=context)
            if project and project.partner_id:
                return {'value': {'partner_id': project.partner_id.id}}
        return {'value': {'partner_id': False}}

    _columns = {
        'id': fields.integer('ID', readonly=True),
        'name': fields.char('Issue', required=True),
        'active': fields.boolean('Active', required=False),
        'create_date': fields.datetime('Creation Date', readonly=True, select=True),
        'write_date': fields.datetime('Update Date', readonly=True),
        'days_since_creation': fields.function(_compute_day, string='Days since creation date', \
                                               multi='compute_day', type="integer", help="Difference in days between creation date and current date"),
        'date_deadline': fields.date('Deadline'),
        'team_id': fields.many2one('crm.team', 'Sales Team', oldname='section_id',\
                        select=True, help='Sales team to which Case belongs to.\
                             Define Responsible user and Email account for mail gateway.'                                                                                         ),
        'partner_id': fields.many2one('res.partner', 'Contact', select=1),
        'company_id': fields.many2one('res.company', 'Company'),
        'description': fields.text('Private Note'),
        'kanban_state': fields.selection([('normal', 'Normal'),('blocked', 'Blocked'),('done', 'Ready for next stage')], 'Kanban State',
                                         track_visibility='onchange',
                                         help="A Issue's kanban state indicates special situations affecting it:\n"
                                              " * Normal is the default situation\n"
                                              " * Blocked indicates something is preventing the progress of this issue\n"
                                              " * Ready for next stage indicates the issue is ready to be pulled to the next stage",
                                         required=True),
        'email_from': fields.char('Email', size=128, help="These people will receive email.", select=1),
        'email_cc': fields.char('Watchers Emails', size=256, help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"),
        'date_open': fields.datetime('Assigned', readonly=True, select=True),
        # Project Issue fields
        'date_closed': fields.datetime('Closed', readonly=True, select=True),
        'date': fields.datetime('Date'),
        'date_last_stage_update': fields.datetime('Last Stage Update', select=True),
        'channel': fields.char('Channel', help="Communication channel."),
        'tag_ids': fields.many2many('project.tags', string='Tags'),
        'priority': fields.selection([('0','Low'), ('1','Normal'), ('2','High')], 'Priority', select=True),
        'stage_id': fields.many2one ('project.task.type', 'Stage',
                        track_visibility='onchange', select=True,
                        domain="[('project_ids', '=', project_id)]", copy=False),
        'project_id': fields.many2one('project.project', 'Project', track_visibility='onchange', select=True),
        'duration': fields.float('Duration'),
        'task_id': fields.many2one('project.task', 'Task', domain="[('project_id','=',project_id)]",
            help="You can link this issue to an existing task or directly create a new one from here"),
        'day_open': fields.function(_compute_day, string='Days to Assign',
                                    multi='compute_day', type="float",
                                    store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_open'], 10)}),
        'day_close': fields.function(_compute_day, string='Days to Close',
                                     multi='compute_day', type="float",
                                     store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_closed'], 10)}),
        'user_id': fields.many2one('res.users', 'Assigned to', required=False, select=1, track_visibility='onchange'),
        'working_hours_open': fields.function(_compute_day, string='Working Hours to assign the Issue',
                                              multi='compute_day', type="float",
                                              store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_open'], 10)}),
        'working_hours_close': fields.function(_compute_day, string='Working Hours to close the Issue',
                                               multi='compute_day', type="float",
                                               store={'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['date_closed'], 10)}),
        'inactivity_days': fields.function(_compute_day, string='Days since last action',
                                           multi='compute_day', type="integer", help="Difference in days between last action and current date"),
        'color': fields.integer('Color Index'),
        'user_email': fields.related('user_id', 'email', type='char', string='User Email', readonly=True),
        'date_action_last': fields.datetime('Last Action', readonly=1),
        'date_action_next': fields.datetime('Next Action', readonly=1),
        'legend_blocked': fields.related("stage_id", "legend_blocked", type="char", string='Kanban Blocked Explanation'),
        'legend_done': fields.related("stage_id", "legend_done", type="char", string='Kanban Valid Explanation'),
        'legend_normal': fields.related("stage_id", "legend_normal", type="char", string='Kanban Ongoing Explanation'),
    }

    _defaults = {
        'active':
        1,
        'team_id':
        lambda s, cr, uid, c: s.pool['crm.team']._get_default_team_id(
            cr, uid, context=c),
        'stage_id':
        lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c),
        'company_id':
        lambda s, cr, uid, c: s.pool['res.users']._get_company(
            cr, uid, context=c),
        'priority':
        '0',
        'kanban_state':
        'normal',
        'date_last_stage_update':
        fields.datetime.now,
        'user_id':
        lambda obj, cr, uid, context: uid,
    }

    _group_by_full = {'stage_id': _read_group_stage_ids}

    def copy(self, cr, uid, id, default=None, context=None):
        issue = self.read(cr, uid, [id], ['name'], context=context)[0]
        if not default:
            default = {}
        default = default.copy()
        default.update(name=_('%s (copy)') % (issue['name']))
        return super(project_issue, self).copy(cr,
                                               uid,
                                               id,
                                               default=default,
                                               context=context)

    def create(self, cr, uid, vals, context=None):
        context = dict(context or {})
        if vals.get('project_id') and not context.get('default_project_id'):
            context['default_project_id'] = vals.get('project_id')
        if vals.get('user_id') and not vals.get('date_open'):
            vals['date_open'] = fields.datetime.now()
        if 'stage_id' in vals:
            vals.update(
                self.onchange_stage_id(cr,
                                       uid,
                                       None,
                                       vals.get('stage_id'),
                                       context=context)['value'])

        # context: no_log, because subtype already handle this
        create_context = dict(context, mail_create_nolog=True)
        return super(project_issue, self).create(cr,
                                                 uid,
                                                 vals,
                                                 context=create_context)

    def write(self, cr, uid, ids, vals, context=None):
        # stage change: update date_last_stage_update
        if 'stage_id' in vals:
            vals.update(
                self.onchange_stage_id(cr,
                                       uid,
                                       ids,
                                       vals.get('stage_id'),
                                       context=context)['value'])
            vals['date_last_stage_update'] = fields.datetime.now()
            if 'kanban_state' not in vals:
                vals['kanban_state'] = 'normal'
        # user_id change: update date_open
        if vals.get('user_id') and 'date_open' not in vals:
            vals['date_open'] = fields.datetime.now()

        return super(project_issue, self).write(cr, uid, ids, vals, context)

    def onchange_task_id(self, cr, uid, ids, task_id, context=None):
        if not task_id:
            return {'value': {}}
        task = self.pool.get('project.task').browse(cr,
                                                    uid,
                                                    task_id,
                                                    context=context)
        return {
            'value': {
                'user_id': task.user_id.id,
            }
        }

    def onchange_partner_id(self, cr, uid, ids, partner_id, context=None):
        """ This function returns value of partner email address based on partner
            :param part: Partner's id
        """
        if partner_id:
            partner = self.pool['res.partner'].browse(cr, uid, partner_id,
                                                      context)
            return {'value': {'email_from': partner.email}}
        return {'value': {'email_from': False}}

    def get_empty_list_help(self, cr, uid, help, context=None):
        context = dict(context or {})
        context['empty_list_help_model'] = 'project.project'
        context['empty_list_help_id'] = context.get('default_project_id')
        context['empty_list_help_document_name'] = _("issues")
        return super(project_issue, self).get_empty_list_help(cr,
                                                              uid,
                                                              help,
                                                              context=context)

    # -------------------------------------------------------
    # Stage management
    # -------------------------------------------------------

    def onchange_stage_id(self, cr, uid, ids, stage_id, context=None):
        if not stage_id:
            return {'value': {}}
        stage = self.pool['project.task.type'].browse(cr,
                                                      uid,
                                                      stage_id,
                                                      context=context)
        if stage.fold:
            return {'value': {'date_closed': fields.datetime.now()}}
        return {'value': {'date_closed': False}}

    def stage_find(self,
                   cr,
                   uid,
                   cases,
                   team_id,
                   domain=[],
                   order='sequence',
                   context=None):
        """ Override of the base.stage method
            Parameter of the stage search taken from the issue:
            - type: stage type must be the same or 'both'
            - team_id: if set, stages must belong to this team or
              be a default case
        """
        if isinstance(cases, (int, long)):
            cases = self.browse(cr, uid, cases, context=context)
        # collect all team_ids
        team_ids = []
        if team_id:
            team_ids.append(team_id)
        for task in cases:
            if task.project_id:
                team_ids.append(task.project_id.id)
        # OR all team_ids and OR with case_default
        search_domain = []
        if team_ids:
            search_domain += [('|')] * (len(team_ids) - 1)
            for team_id in team_ids:
                search_domain.append(('project_ids', '=', team_id))
        search_domain += list(domain)
        # perform search, return the first found
        stage_ids = self.pool.get('project.task.type').search(cr,
                                                              uid,
                                                              search_domain,
                                                              order=order,
                                                              context=context)
        if stage_ids:
            return stage_ids[0]
        return False

    # -------------------------------------------------------
    # Mail gateway
    # -------------------------------------------------------

    def _track_subtype(self, cr, uid, ids, init_values, context=None):
        record = self.browse(cr, uid, ids[0], context=context)
        if 'kanban_state' in init_values and record.kanban_state == 'blocked':
            return 'project_issue.mt_issue_blocked'
        elif 'kanban_state' in init_values and record.kanban_state == 'done':
            return 'project_issue.mt_issue_ready'
        elif 'user_id' in init_values and record.user_id:  # assigned -> new
            return 'project_issue.mt_issue_new'
        elif 'stage_id' in init_values and record.stage_id and record.stage_id.sequence <= 1:  # start stage -> new
            return 'project_issue.mt_issue_new'
        elif 'stage_id' in init_values:
            return 'project_issue.mt_issue_stage'
        return super(project_issue, self)._track_subtype(cr,
                                                         uid,
                                                         ids,
                                                         init_values,
                                                         context=context)

    def _notification_group_recipients(self,
                                       cr,
                                       uid,
                                       ids,
                                       message,
                                       recipients,
                                       done_ids,
                                       group_data,
                                       context=None):
        """ Override the mail.thread method to handle project users and officers
        recipients. Indeed those will have specific action in their notification
        emails: creating tasks, assigning it. """
        group_project_user = self.pool['ir.model.data'].xmlid_to_res_id(
            cr, uid, 'project.group_project_user')
        for recipient in recipients:
            if recipient.id in done_ids:
                continue
            if recipient.user_ids and group_project_user in recipient.user_ids[
                    0].groups_id.ids:
                group_data['group_project_user'] |= recipient
                done_ids.add(recipient.id)
        return super(project_issue,
                     self)._notification_group_recipients(cr,
                                                          uid,
                                                          ids,
                                                          message,
                                                          recipients,
                                                          done_ids,
                                                          group_data,
                                                          context=context)

    def _notification_get_recipient_groups(self,
                                           cr,
                                           uid,
                                           ids,
                                           message,
                                           recipients,
                                           context=None):
        res = super(project_issue,
                    self)._notification_get_recipient_groups(cr,
                                                             uid,
                                                             ids,
                                                             message,
                                                             recipients,
                                                             context=context)

        new_action_id = self.pool['ir.model.data'].xmlid_to_res_id(
            cr, uid, 'project_issue.project_issue_categ_act0')
        take_action = self._notification_link_helper(cr,
                                                     uid,
                                                     ids,
                                                     'assign',
                                                     context=context)
        new_action = self._notification_link_helper(cr,
                                                    uid,
                                                    ids,
                                                    'new',
                                                    context=context,
                                                    action_id=new_action_id)

        task_record = self.browse(cr, uid, ids[0], context=context)
        actions = []
        if not task_record.user_id:
            actions.append({'url': take_action, 'title': _('I take it')})
        else:
            actions.append({'url': new_action, 'title': _('New Issue')})

        res['group_project_user'] = {'actions': actions}
        return res

    @api.cr_uid_context
    def message_get_reply_to(self, cr, uid, ids, default=None, context=None):
        """ Override to get the reply_to of the parent project. """
        issues = self.browse(cr, SUPERUSER_ID, ids, context=context)
        project_ids = set(
            [issue.project_id.id for issue in issues if issue.project_id])
        aliases = self.pool['project.project'].message_get_reply_to(
            cr, uid, list(project_ids), default=default, context=context)
        return dict(
            (issue.id,
             aliases.get(issue.project_id and issue.project_id.id or 0, False))
            for issue in issues)

    def message_get_suggested_recipients(self, cr, uid, ids, context=None):
        recipients = super(project_issue,
                           self).message_get_suggested_recipients(
                               cr, uid, ids, context=context)
        try:
            for issue in self.browse(cr, uid, ids, context=context):
                if issue.partner_id:
                    issue._message_add_suggested_recipient(
                        recipients,
                        partner=issue.partner_id,
                        reason=_('Customer'))
                elif issue.email_from:
                    issue._message_add_suggested_recipient(
                        recipients,
                        email=issue.email_from,
                        reason=_('Customer Email'))
        except AccessError:  # no read access rights -> just ignore suggested recipients because this imply modifying followers
            pass
        return recipients

    def email_split(self, cr, uid, ids, msg, context=None):
        email_list = tools.email_split((msg.get('to') or '') + ',' +
                                       (msg.get('cc') or ''))
        # check left-part is not already an alias
        issue_ids = self.browse(cr, uid, ids, context=context)
        aliases = [
            issue.project_id.alias_name for issue in issue_ids
            if issue.project_id
        ]
        return filter(lambda x: x.split('@')[0] not in aliases, email_list)

    def message_new(self, cr, uid, msg, custom_values=None, context=None):
        """ Overrides mail_thread message_new that is called by the mailgateway
            through message_process.
            This override updates the document according to the email.
        """
        if custom_values is None:
            custom_values = {}
        context = dict(context or {}, state_to='draft')
        defaults = {
            'name': msg.get('subject') or _("No Subject"),
            'email_from': msg.get('from'),
            'email_cc': msg.get('cc'),
            'partner_id': msg.get('author_id', False),
            'user_id': False,
        }
        defaults.update(custom_values)

        res_id = super(project_issue, self).message_new(cr,
                                                        uid,
                                                        msg,
                                                        custom_values=defaults,
                                                        context=context)
        email_list = self.email_split(cr, uid, [res_id], msg, context=context)
        partner_ids = self._find_partner_from_emails(cr,
                                                     uid, [res_id],
                                                     email_list,
                                                     force_create=True,
                                                     context=context)
        self.message_subscribe(cr, uid, [res_id], partner_ids, context=context)
        return res_id

    def message_update(self,
                       cr,
                       uid,
                       ids,
                       msg,
                       update_vals=None,
                       context=None):
        """ Override to update the issue according to the email. """

        email_list = self.email_split(cr, uid, ids, msg, context=context)
        partner_ids = self._find_partner_from_emails(cr,
                                                     uid,
                                                     ids,
                                                     email_list,
                                                     force_create=True,
                                                     context=context)
        self.message_subscribe(cr, uid, ids, partner_ids, context=context)
        return super(project_issue,
                     self).message_update(cr,
                                          uid,
                                          ids,
                                          msg,
                                          update_vals=update_vals,
                                          context=context)

    @api.cr_uid_ids_context
    @api.returns('mail.message', lambda value: value.id)
    def message_post(self,
                     cr,
                     uid,
                     thread_id,
                     subtype=None,
                     context=None,
                     **kwargs):
        """ Overrides mail_thread message_post so that we can set the date of last action field when
            a new message is posted on the issue.
        """
        if context is None:
            context = {}
        res = super(project_issue, self).message_post(cr,
                                                      uid,
                                                      thread_id,
                                                      subtype=subtype,
                                                      context=context,
                                                      **kwargs)
        if thread_id and subtype:
            self.write(cr,
                       SUPERUSER_ID,
                       thread_id, {'date_action_last': fields.datetime.now()},
                       context=context)
        return res
Ejemplo n.º 29
0
    return dict((id, 3) for id in ids)

def function_fn_write(model, cr, uid, id, field_name, field_value, fnct_inv_arg, context):
    """ just so CreatorCase.export can be used
    """
    pass

models = [
    ('boolean', fields.boolean()),
    ('integer', fields.integer()),
    ('float', fields.float()),
    ('decimal', fields.float(digits=(16, 3))),
    ('string.bounded', fields.char('unknown', size=16)),
    ('string.required', fields.char('unknown', size=None, required=True)),
    ('string', fields.char('unknown', size=None)),
    ('date', fields.date()),
    ('datetime', fields.datetime()),
    ('text', fields.text()),
    ('selection', fields.selection([(1, "Foo"), (2, "Bar"), (3, "Qux"), (4, '')])),
    # here use size=-1 to store the values as integers instead of strings
    ('selection.function', fields.selection(selection_fn, size=-1)),
    # just relate to an integer
    ('many2one', fields.many2one('export.integer')),
    ('one2many', fields.one2many('export.one2many.child', 'parent_id')),
    ('many2many', fields.many2many('export.many2many.other')),
    ('function', fields.function(function_fn, fnct_inv=function_fn_write, type="integer")),
    # related: specialization of fields.function, should work the same way
    # TODO: reference
]

for name, field in models:
Ejemplo n.º 30
0
class payment_advice_report(osv.osv):
    _name = "payment.advice.report"
    _description = "Payment Advice Analysis"
    _auto = False
    _columns = {
        'name':
        fields.char('Name', readonly=True),
        'date':
        fields.date(
            'Date',
            readonly=True,
        ),
        'year':
        fields.char('Year', size=4, readonly=True),
        'month':
        fields.selection([('01', 'January'), ('02', 'February'),
                          ('03', 'March'), ('04', 'April'), ('05', 'May'),
                          ('06', 'June'), ('07', 'July'), ('08', 'August'),
                          ('09', 'September'), ('10', 'October'),
                          ('11', 'November'), ('12', 'December')],
                         'Month',
                         readonly=True),
        'day':
        fields.char('Day', size=128, readonly=True),
        'state':
        fields.selection([
            ('draft', 'Draft'),
            ('confirm', 'Confirmed'),
            ('cancel', 'Cancelled'),
        ],
                         'Status',
                         select=True,
                         readonly=True),
        'employee_id':
        fields.many2one('hr.employee', 'Employee', readonly=True),
        'nbr':
        fields.integer('# Payment Lines', readonly=True),
        'number':
        fields.char('Number', readonly=True),
        'bysal':
        fields.float('By Salary', readonly=True),
        'bank_id':
        fields.many2one('res.bank', 'Bank', readonly=True),
        'company_id':
        fields.many2one('res.company', 'Company', readonly=True),
        'cheque_nos':
        fields.char('Cheque Numbers', readonly=True),
        'neft':
        fields.boolean('NEFT Transaction', readonly=True),
        'ifsc_code':
        fields.char('IFSC Code', size=32, readonly=True),
        'employee_bank_no':
        fields.char('Employee Bank Account', required=True),
    }

    def init(self, cr):
        tools.drop_view_if_exists(cr, 'payment_advice_report')
        cr.execute("""
            create or replace view payment_advice_report as (
                select
                    min(l.id) as id,
                    sum(l.bysal) as bysal,
                    p.name,
                    p.state,
                    p.date,
                    p.number,
                    p.company_id,
                    p.bank_id,
                    p.chaque_nos as cheque_nos,
                    p.neft,
                    l.employee_id,
                    l.ifsc_code,
                    l.name as employee_bank_no,
                    to_char(p.date, 'YYYY') as year,
                    to_char(p.date, 'MM') as month,
                    to_char(p.date, 'YYYY-MM-DD') as day,
                    1 as nbr
                from
                    hr_payroll_advice as p
                    left join hr_payroll_advice_line as l on (p.id=l.advice_id)
                where 
                    l.employee_id IS NOT NULL
                group by
                    p.number,p.name,p.date,p.state,p.company_id,p.bank_id,p.chaque_nos,p.neft,
                    l.employee_id,l.advice_id,l.bysal,l.ifsc_code, l.name
            )
        """)
Ejemplo n.º 31
0
class hr_employee(osv.osv):
    _name = "hr.employee"
    _description = "Employee"
    _order = 'name_related'
    _inherits = {'resource.resource': "resource_id"}
    _inherit = ['mail.thread']

    _mail_post_access = 'read'

    _columns = {
        #we need a related field in order to be able to sort the employee by name
        'name_related':
        fields.related('resource_id',
                       'name',
                       type='char',
                       string='Name',
                       readonly=True,
                       store=True),
        'country_id':
        fields.many2one('res.country', 'Nationality (Country)'),
        'birthday':
        fields.date("Date of Birth"),
        'ssnid':
        fields.char('SSN No', help='Social Security Number'),
        'sinid':
        fields.char('SIN No', help="Social Insurance Number"),
        'identification_id':
        fields.char('Identification No'),
        'gender':
        fields.selection([('male', 'Male'), ('female', 'Female'),
                          ('other', 'Other')], 'Gender'),
        'marital':
        fields.selection([('single', 'Single'), ('married', 'Married'),
                          ('widower', 'Widower'), ('divorced', 'Divorced')],
                         'Marital Status'),
        'department_id':
        fields.many2one('hr.department', 'Department'),
        'address_id':
        fields.many2one('res.partner', 'Working Address'),
        'address_home_id':
        fields.many2one('res.partner', 'Home Address'),
        'bank_account_id':
        fields.many2one('res.partner.bank',
                        'Bank Account Number',
                        domain="[('partner_id','=',address_home_id)]",
                        help="Employee bank salary account"),
        'work_phone':
        fields.char('Work Phone', readonly=False),
        'mobile_phone':
        fields.char('Work Mobile', readonly=False),
        'work_email':
        fields.char('Work Email', size=240),
        'work_location':
        fields.char('Work Location'),
        'notes':
        fields.text('Notes'),
        'parent_id':
        fields.many2one('hr.employee', 'Manager'),
        'category_ids':
        fields.many2many('hr.employee.category', 'employee_category_rel',
                         'emp_id', 'category_id', 'Tags'),
        'child_ids':
        fields.one2many('hr.employee', 'parent_id', 'Subordinates'),
        'resource_id':
        fields.many2one('resource.resource',
                        'Resource',
                        ondelete='cascade',
                        required=True,
                        auto_join=True),
        'coach_id':
        fields.many2one('hr.employee', 'Coach'),
        'job_id':
        fields.many2one('hr.job', 'Job Title'),
        'passport_id':
        fields.char('Passport No'),
        'color':
        fields.integer('Color Index'),
        'city':
        fields.related('address_id', 'city', type='char', string='City'),
        'login':
        fields.related('user_id',
                       'login',
                       type='char',
                       string='Login',
                       readonly=1),
        'last_login':
        fields.related('user_id',
                       'date',
                       type='datetime',
                       string='Latest Connection',
                       readonly=1),
    }

    # image: all image fields are base64 encoded and PIL-supported
    image = ecore.fields.Binary(
        "Photo",
        attachment=True,
        help=
        "This field holds the image used as photo for the employee, limited to 1024x1024px."
    )
    image_medium = ecore.fields.Binary("Medium-sized photo",
        compute='_compute_images', inverse='_inverse_image_medium', store=True, attachment=True,
        help="Medium-sized photo of the employee. It is automatically "\
             "resized as a 128x128px image, with aspect ratio preserved. "\
             "Use this field in form views or some kanban views.")
    image_small = ecore.fields.Binary("Small-sized photo",
        compute='_compute_images', inverse='_inverse_image_small', store=True, attachment=True,
        help="Small-sized photo of the employee. It is automatically "\
             "resized as a 64x64px image, with aspect ratio preserved. "\
             "Use this field anywhere a small image is required.")

    @api.depends('image')
    def _compute_images(self):
        for rec in self:
            rec.image_medium = tools.image_resize_image_medium(rec.image)
            rec.image_small = tools.image_resize_image_small(rec.image)

    def _inverse_image_medium(self):
        for rec in self:
            rec.image = tools.image_resize_image_big(rec.image_medium)

    def _inverse_image_small(self):
        for rec in self:
            rec.image = tools.image_resize_image_big(rec.image_small)

    def _get_default_image(self, cr, uid, context=None):
        image_path = get_module_resource('hr', 'static/src/img',
                                         'default_image.png')
        return tools.image_resize_image_big(
            open(image_path, 'rb').read().encode('base64'))

    defaults = {
        'active': 1,
        'image': _get_default_image,
        'color': 0,
    }

    def unlink(self, cr, uid, ids, context=None):
        resource_ids = []
        for employee in self.browse(cr, uid, ids, context=context):
            resource_ids.append(employee.resource_id.id)
        super(hr_employee, self).unlink(cr, uid, ids, context=context)
        return self.pool.get('resource.resource').unlink(cr,
                                                         uid,
                                                         resource_ids,
                                                         context=context)

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

    def onchange_company(self, cr, uid, ids, company, context=None):
        address_id = False
        if company:
            company_id = self.pool.get('res.company').browse(cr,
                                                             uid,
                                                             company,
                                                             context=context)
            address = self.pool.get('res.partner').address_get(
                cr, uid, [company_id.partner_id.id], ['contact'])
            address_id = address and address['contact'] or False
        return {'value': {'address_id': address_id}}

    def onchange_department_id(self,
                               cr,
                               uid,
                               ids,
                               department_id,
                               context=None):
        value = {'parent_id': False}
        if department_id:
            department = self.pool.get('hr.department').browse(
                cr, uid, department_id)
            value['parent_id'] = department.manager_id.id
        return {'value': value}

    def onchange_user(self, cr, uid, ids, name, image, user_id, context=None):
        if user_id:
            user = self.pool['res.users'].browse(cr,
                                                 uid,
                                                 user_id,
                                                 context=context)
            values = {
                'name': name or user.name,
                'work_email': user.email,
                'image': image or user.image,
            }
            return {'value': values}

    def action_follow(self, cr, uid, ids, context=None):
        """ Wrapper because message_subscribe_users take a user_ids=None
            that receive the context without the wrapper. """
        return self.message_subscribe_users(cr, uid, ids, context=context)

    def action_unfollow(self, cr, uid, ids, context=None):
        """ Wrapper because message_unsubscribe_users take a user_ids=None
            that receive the context without the wrapper. """
        return self.message_unsubscribe_users(cr, uid, ids, context=context)

    def _message_get_auto_subscribe_fields(self,
                                           cr,
                                           uid,
                                           updated_fields,
                                           auto_follow_fields=None,
                                           context=None):
        """ Overwrite of the original method to always follow user_id field,
        even when not track_visibility so that a user will follow it's employee
        """
        if auto_follow_fields is None:
            auto_follow_fields = ['user_id']
        user_field_lst = []
        for name, field in self._fields.items():
            if name in auto_follow_fields and name in updated_fields and field.comodel_name == 'res.users':
                user_field_lst.append(name)
        return user_field_lst

    _constraints = [
        (osv.osv._check_recursion,
         _('Error! You cannot create recursive hierarchy of Employee(s).'),
         ['parent_id']),
    ]