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'
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
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)
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)
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)
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), }
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 ) """)
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)
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
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)
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
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)
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)
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')) )""")
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 )
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(), }
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)
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']) ]
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)
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
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}
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
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 )""")
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""")
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
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
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:
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 ) """)
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']), ]