class OpBatch(models.Model): _name = 'op.batch' code = fields.Char('Code', size=8, required=True) name = fields.Char('Name', size=32, required=True) start_date = fields.Date('Start Date', required=True, default=fields.Date.today()) end_date = fields.Date('End Date', required=True) course_id = fields.Many2one('op.course', 'Course', required=True) @api.one @api.constrains('start_date', 'end_date') def check_dates(self): start_date = fields.Date.from_string(self.start_date) end_date = fields.Date.from_string(self.end_date) if start_date > end_date: raise ValidationError("End Date cannot be set before Start Date.") @api.model def name_search(self, name, args=None, operator='ilike', limit=100): if self.env.context.get('get_parent_batch', False): lst = [] lst.append(self.env.context.get('course_id')) courses = self.env['op.course'].browse(lst) while courses.parent_id: lst.append(courses.parent_id.id) courses = courses.parent_id batches = self.env['op.batch'].search([('course_id', 'in', lst)]) return batches.name_get() return super(OpBatch, self).name_search(name, args, operator=operator, limit=limit)
class StudentAttendance(models.TransientModel): _name = 'student.attendance' from_date = fields.Date('From Date', required=True, default=lambda self: fields.Date.today()) to_date = fields.Date('To Date', required=True, default=lambda self: fields.Date.today()) @api.one @api.constrains('from_date', 'to_date') def check_dates(self): from_date = fields.Date.from_string(self.from_date) to_date = fields.Date.from_string(self.to_date) if to_date < from_date: raise ValidationError("To Date cannot be set before From Date.") @api.multi def print_report(self): data = self.read(['from_date', 'to_date'])[0] data.update({'student_id': self.env.context.get('active_id', False)}) return self.env['report'].get_action( self, 'openeducat_attendance.student_attendance_report', data=data)
class OpBookQueue(models.Model): _name = 'op.book.queue' _inherit = 'mail.thread' _rec_name = 'user_id' _description = 'Book Queue Request' name = fields.Char("Sequence No", readonly=True, copy=False, default='/') partner_id = fields.Many2one('res.partner', 'Student/Faculty') book_id = fields.Many2one('op.book', 'Book', required=True, track_visibility='onchange') date_from = fields.Date('From Date', required=True, default=fields.Date.today()) date_to = fields.Date('To Date', required=True) user_id = fields.Many2one('res.users', 'User', readonly=True, default=lambda self: self.env.uid) state = fields.Selection([('request', 'Request'), ('accept', 'Accepted'), ('reject', 'Rejected')], 'Status', copy=False, default='request', track_visibility='onchange') @api.onchange('user_id') def onchange_user(self): self.partner_id = self.user_id.partner_id.id @api.constrains('date_from', 'date_to') def _check_date(self): if self.date_from > self.date_to: raise ValidationError('To Date cannot be set before From Date.') @api.model def create(self, vals): if vals.get('name', '/') == '/': vals['name'] = self.env['ir.sequence'].next_by_code( 'op.book.queue') or '/' return super(OpBookQueue, self).create(vals) @api.one def do_reject(self): self.state = 'reject' @api.one def do_accept(self): self.state = 'accept' @api.one def do_request_again(self): self.state = 'request'
class OpPlacementOffer(models.Model): _name = 'op.placement.offer' _inherit = 'mail.thread' _description = 'Placement Offer' name = fields.Char('Company Name', required=True) student_id = fields.Many2one('op.student', 'Student Name', required=True) join_date = fields.Date('Join Date', default=fields.Date.today()) offer_package = fields.Char('Offered Package', size=256) training_period = fields.Char('Training Period', size=256) state = fields.Selection( [('draft', 'Draft'), ('offer', 'Offer'), ('join', 'Join'), ('reject', 'Rejected'), ('cancel', 'Cancel')], 'State', default='draft', track_visibility='onchange') @api.one def placement_offer(self): self.state = 'offer' @api.one def placement_join(self): self.state = 'join' @api.one def confirm_rejected(self): self.state = 'reject' @api.one def confirm_to_draft(self): self.state = 'draft' @api.one def confirm_cancel(self): self.state = 'cancel'
class ReturnBook(models.TransientModel): """ Retrun Book Wizard """ _name = 'return.book' book_id = fields.Many2one('op.book', 'Book', readonly=True) book_unit_id = fields.Many2one( 'op.book.unit', 'Book Unit', readonly=True, required=True) actual_return_date = fields.Date( 'Actual Return Date', default=lambda self: fields.Date.today(), required=True) @api.one def do_return(self): book_movement = self.env['op.book.movement'] if self.book_unit_id.state and self.book_unit_id.state == 'issue': book_move_search = book_movement.search( [('book_unit_id', '=', self.book_unit_id.id), ('state', '=', 'issue')]) if not book_move_search: return {'type': 'ir.actions.act_window_close'} book_move_search.actual_return_date = self.actual_return_date book_move_search.calculate_penalty() book_move_search.state = 'return' self.book_unit_id.state = 'available' else: raise UserError(_( "Book Unit can not be returned because it's state is : %s") % (dict(book_unit.unit_states).get(self.book_unit_id.state)))
class OpHostelRoomAllocation(models.Model): _name = 'op.hostel.room' hostel_id = fields.Many2one('op.hostel', 'Hostel', required=True) name = fields.Many2one('op.room', 'Room', required=True) student_ids = fields.Many2many('res.partner', string='Allocated Students') students_per_room = fields.Integer('Students per Room', required=True) rent = fields.Float('Rent') allocated_date = fields.Date('Allocated Date', default=fields.Date.today()) @api.constrains('students_per_room') def check_capacity(self): if self.students_per_room <= 0: raise ValidationError("Enter proper Student Per Room") @api.onchange('hostel_id') def onchange_hostel(self): if self.hostel_id: self.name = False @api.onchange('name') def onchange_name(self): if self.name: self.students_per_room = self.name.capacity @api.one @api.constrains('student_ids', 'students_per_room') def _check_student_capacity(self): if len(self.student_ids) > self.students_per_room: raise ValidationError('Room capacity Over')
class OpLibraryCard(models.Model): _name = 'op.library.card' _rec_name = 'number' _description = 'Library Card' partner_id = fields.Many2one('res.partner', 'Student/Faculty', required=True) number = fields.Char('Number', size=256, required=True) library_card_type_id = fields.Many2one('op.library.card.type', 'Card Type', required=True) issue_date = fields.Date('Issue Date', required=True, default=fields.Date.today()) type = fields.Selection([('student', 'Student'), ('faculty', 'Faculty')], 'Type', default='student', required=True) student_id = fields.Many2one('op.student', 'Student') faculty_id = fields.Many2one('op.faculty', 'Faculty') _sql_constraints = [ ('unique_library_card_number', 'unique(number)', 'Library card Number should be unique per card!'), ]
class OpAttendanceLine(models.Model): _name = 'op.attendance.line' _rec_name = 'attendance_id' attendance_id = fields.Many2one( 'op.attendance.sheet', 'Attendance Sheet', required=True) student_id = fields.Many2one('op.student', 'Student', required=True) present = fields.Boolean('Present ?', default=True) course_id = fields.Many2one( 'op.course', 'Course', related='attendance_id.register_id.course_id', store=True, readonly=True) batch_id = fields.Many2one( 'op.batch', 'Batch', related='attendance_id.register_id.batch_id', store=True, readonly=True) remark = fields.Char('Remark', size=256) attendance_date = fields.Date( 'Date', related='attendance_id.attendance_date', store=True, readonly=True) _sql_constraints = [ ('unique_student', 'unique(student_id,attendance_id,attendance_date)', 'Student must be unique per Attendance.'), ]
class OpHealthLine(models.Model): _name = 'op.health.line' health_id = fields.Many2one('op.health', 'Health') date = fields.Date('Date', default=lambda self: fields.Date.today()) name = fields.Text('Checkup Detail', required=True) recommendation = fields.Text('Checkup Recommendation')
class wx_qyh_extend(models.Model): _inherit = "wx.qyh" operator_name = fields.Char(string="运营者姓名") register_mobile = fields.Char(string="手机号") register_email = fields.Char(string="邮箱") auth_time = fields.Date(string="认证时间") wx_number = fields.Char(string="微信号") cus_phone = fields.Char(string="客服电话")
class OpStudent(models.Model): _name = 'op.student' _inherits = {'res.partner': 'partner_id'} @api.one @api.depends('roll_number_line', 'batch_id', 'course_id') def _get_curr_roll_number(self): # TO_DO:: Improve the logic by adding sequence field in course. if self.roll_number_line: for roll_no in self.roll_number_line: if roll_no.course_id == self.course_id and \ roll_no.batch_id == self.batch_id: self.roll_number = roll_no.roll_number else: self.roll_number = 0 middle_name = fields.Char('Middle Name', size=128) last_name = fields.Char('Last Name', size=128, required=True) birth_date = fields.Date('Birth Date', required=True) blood_group = fields.Selection([('A+', 'A+ve'), ('B+', 'B+ve'), ('O+', 'O+ve'), ('AB+', 'AB+ve'), ('A-', 'A-ve'), ('B-', 'B-ve'), ('O-', 'O-ve'), ('AB-', 'AB-ve')], 'Blood Group') gender = fields.Selection([('m', 'Male'), ('f', 'Female'), ('o', 'Other')], 'Gender', required=True) nationality = fields.Many2one('res.country', 'Nationality') emergency_contact = fields.Many2one('res.partner', 'Emergency Contact') visa_info = fields.Char('Visa Info', size=64) id_number = fields.Char('ID Card Number', size=64) photo = fields.Binary('Photo') course_id = fields.Many2one('op.course', 'Course', required=True) batch_id = fields.Many2one('op.batch', 'Batch', required=True) roll_number_line = fields.One2many('op.roll.number', 'student_id', 'Roll Number') partner_id = fields.Many2one('res.partner', 'Partner', required=True, ondelete="cascade") roll_number = fields.Char('Current Roll Number', compute='_get_curr_roll_number', size=8, store=True) gr_no = fields.Char("GR Number", size=20) @api.one @api.constrains('birth_date') def _check_birthdate(self): if self.birth_date > fields.Date.today(): raise ValidationError( "Birth Date can't be greater than current date!") @api.onchange('course_id') def onchange_course(self): self.batch_id = False
class report_plm_document_wall(models.Model): _name = "report.plm_document.wall" _description = "Users that did not inserted documents since one month" _auto = False name = fields.Date(_('Month'), readonly=True) user_id = fields.Many2one('res.users', _('Owner'), readonly=True) user = fields.Char(_('User'), size=64, readonly=True) month = fields.Char(_('Month'), size=24, readonly=True) last = fields.Datetime(_('Last Posted Time'), readonly=True)
class OpActivity(models.Model): _name = 'op.activity' _rec_name = 'student_id' _inherit = 'mail.thread' student_id = fields.Many2one('op.student', 'Student', required=True) faculty_id = fields.Many2one('op.faculty', 'Faculty') type_id = fields.Many2one('op.activity.type', 'Activity Type') description = fields.Text('Description') date = fields.Date('Date', default=fields.Date.today())
class OpAchievement(models.Model): _name = 'op.achievement' _rec_name = 'student_id' student_id = fields.Many2one('op.student', 'Student', required=True) faculty_id = fields.Many2one('op.faculty', 'Faculty', required=True) achievement_type = fields.Many2one( 'op.achievement.type', 'Achievement Type', required=True) description = fields.Text('Description', required=True) achievement_date = fields.Date( 'Date', required=True, default=fields.Date.today())
class OpFaculty(models.Model): _name = 'op.faculty' _inherits = {'res.partner': 'partner_id'} partner_id = fields.Many2one('res.partner', 'Partner', required=True, ondelete="cascade") middle_name = fields.Char('Middle Name', size=128) last_name = fields.Char('Last Name', size=128, required=True) birth_date = fields.Date('Birth Date', required=True) blood_group = fields.Selection([('A+', 'A+ve'), ('B+', 'B+ve'), ('O+', 'O+ve'), ('AB+', 'AB+ve'), ('A-', 'A-ve'), ('B-', 'B-ve'), ('O-', 'O-ve'), ('AB-', 'AB-ve')], 'Blood Group') gender = fields.Selection([('male', 'Male'), ('female', 'Female')], 'Gender', required=True) nationality = fields.Many2one('res.country', 'Nationality') emergency_contact = fields.Many2one('res.partner', 'Emergency Contact') visa_info = fields.Char('Visa Info', size=64) id_number = fields.Char('ID Card Number', size=64) photo = fields.Binary('Photo') login = fields.Char('Login', related='partner_id.user_id.login', readonly=1) last_login = fields.Datetime('Latest Connection', related='partner_id.user_id.login_date', readonly=1) faculty_subject_ids = fields.Many2many('op.subject', string='Subject(s)') emp_id = fields.Many2one('hr.employee', 'Employee') @api.one @api.constrains('birth_date') def _check_birthdate(self): if self.birth_date > fields.Date.today(): raise ValidationError( "Birth Date can't be greater than current date!") @api.one def create_employee(self): vals = { 'name': self.name + ' ' + (self.middle_name or '') + ' ' + self.last_name, 'country_id': self.nationality.id, 'gender': self.gender, 'address_home_id': self.partner_id.id } emp_id = self.env['hr.employee'].create(vals) self.write({'emp_id': emp_id.id}) self.partner_id.write({'supplier': True, 'employee': True})
class OpExamSession(models.Model): _name = 'op.exam.session' _description = 'Exam Session' name = fields.Char('Exam', size=256, required=True) course_id = fields.Many2one('op.course', 'Course', required=True) batch_id = fields.Many2one('op.batch', 'Batch', required=True) exam_code = fields.Char('Exam Code', size=8, required=True) start_date = fields.Date('Start Date', required=True) end_date = fields.Date('End Date', required=True) room_id = fields.Many2one('op.exam.room', 'Room', required=True) exam_ids = fields.One2many('op.exam', 'session_id', 'Exam(s)') @api.constrains('start_date', 'end_date') def _check_date_time(self): if self.start_date > self.end_date: raise ValidationError( 'End Date cannot be set before Start Date.') @api.onchange('course_id') def onchange_course(self): self.batch_id = False
class AdmissionAnalysis(models.TransientModel): """ Admission Analysis Wizard """ _name = 'admission.analysis' course_id = fields.Many2one('op.course', 'Course', required=True) start_date = fields.Date('Start Date', default=time.strftime('%Y-%m-01'), required=True) end_date = fields.Date('End Date', required=True) @api.multi def print_report(self): start_date = fields.Date.from_string(self.start_date) end_date = fields.Date.from_string(self.end_date) if start_date > end_date: raise ValidationError("End Date cannot be set before Start Date.") else: data = self.read(['course_id', 'start_date', 'end_date'])[0] return self.env['report'].get_action( self, 'openeducat_admission.report_admission_analysis', data=data)
class training(models.Model): ''' 实体:培训记录 ''' _name = 'hr.training.record' lession_id = fields.Many2one( 'hr.training.lession', string='培训课程', required=True, ) date = fields.Date(required=True, string='培训日期') time = fields.Char(required=True, string='培训时间') address = fields.Char(required=True, string='培训地点') teacher = fields.Char(required=True, string='培训讲师') student = fields.Many2many('hr.employee', 'training_employee_rel', 'training_id', 'employee_id', required=True, string='培训学员') price = fields.Float(digits=(16, 2), string='培训费用', default=0.0) description = fields.Text(string='培训描述') @api.onchange('lession_id') def change_lession(self): ''' 功能:默认根据课程获取培训费用、培训讲师 :return: ''' if self.lession_id: self.price = self.lession_id.price teachers = '' if self.lession_id.external_teacher: teachers = self.lession_id.external_teacher + ',' #取内聘讲师 sql_str = '''select emp.name_related from lession_teacher_rel as r join hr_employee as emp on r.teacher_id=emp.id where r.lession_id=%s ''' % (self.lession_id.id) _logger.info('根据培训课程ID,查找讲师=%s' % sql_str) self._cr.execute(sql_str) res = self._cr.fetchall() for r in res: teachers += r[0] + ',' if len(teachers) > 0: teachers = teachers[0:-1] self.teacher = teachers self.description = self.lession_id.description
class link_tracker_click(models.Model): _name = "link.tracker.click" _rec_name = "link_id" click_date = fields.Date(string='Create Date') link_id = fields.Many2one('link.tracker', 'Link', required=True, ondelete='cascade') ip = fields.Char(string='Internet Protocol') country_id = fields.Many2one('res.country', 'Country') @api.model def add_click(self, code, ip, country_code, stat_id=False): self = self.sudo() code_rec = self.env['link.tracker.code'].search([('code', '=', code)]) if not code_rec: return None again = self.search_count([('link_id', '=', code_rec.link_id.id), ('ip', '=', ip)]) if not again: country_record = self.env['res.country'].search( [('code', '=', country_code)], limit=1) vals = { 'link_id': code_rec.link_id.id, 'create_date': datetime.date.today(), 'ip': ip, 'country_id': country_record.id, 'mail_stat_id': stat_id } if stat_id: mail_stat = self.env['mail.mail.statistics'].search([ ('id', '=', stat_id) ]) if mail_stat.mass_mailing_campaign_id: vals[ 'mass_mailing_campaign_id'] = mail_stat.mass_mailing_campaign_id.id if mail_stat.mass_mailing_id: vals['mass_mailing_id'] = mail_stat.mass_mailing_id.id self.create(vals)
class ReturnDate(models.TransientModel): """ Assign return date """ _name = 'return.date' actual_return_date = fields.Date('Actual Return Date', required=True, default=lambda self: fields.Date.today()) @api.one def assign_return_date(self): book_movement = self.env['op.book.movement'].browse( self.env.context.get('active_ids', False)) book_movement.write({'actual_return_date': self.actual_return_date}) book_movement.calculate_penalty() book_movement.state = 'return' book_movement.book_unit_id.state = 'available'
class StudentMigrate(models.TransientModel): """ Student Migration Wizard """ _name = 'student.migrate' date = fields.Date('Date', required=True, default=fields.Date.today()) course_from_id = fields.Many2one('op.course', 'From Course', required=True) course_to_id = fields.Many2one('op.course', 'To Course', required=True) student_ids = fields.Many2many('op.student', string='Student(s)', required=True) @api.one @api.constrains('course_from_id', 'course_to_id') def _check_admission_register(self): if self.course_from_id == self.course_to_id: raise ValidationError("From Course must not be same as To Course!") if self.course_from_id.parent_id: if self.course_from_id.parent_id != \ self.course_to_id.parent_id: raise ValidationError( "Can't migrate, As selected courses don't \ share same parent course!") else: raise ValidationError("Can't migrate, Proceed for new admission") @api.one @api.onchange('course_from_id') def onchange_course_id(self): self.student_ids = False @api.one def student_migrate_forward(self): activity_type = self.env["op.activity.type"] act_type = activity_type.search([('name', '=', 'Migration')], limit=1) if not act_type: act_type = activity_type.create({'name': 'Migration'}) for student in self.student_ids: activity_vals = { 'student_id': student.id, 'type_id': act_type.id, 'date': self.date } self.env['op.activity'].create(activity_vals) student.write({'course_id': self.course_to_id.id})
class OpMarksheetRegister(models.Model): _name = 'op.marksheet.register' exam_session_id = fields.Many2one( 'op.exam.session', 'Exam', required=True) marksheet_line = fields.One2many( 'op.marksheet.line', 'marksheet_reg_id', 'Marksheets') generated_date = fields.Date( 'Generated Date', required=True, default=fields.Date.today()) generated_by = fields.Many2one( 'res.users', 'Generated By', default=lambda self: self.env.uid, required=True) status = fields.Selection( [('draft', 'Draft'), ('validated', 'Validated'), ('cancelled', 'Cancelled')], 'Status', default="draft", required=True) total_pass = fields.Float('Total Pass') total_failed = fields.Float('Total Fail') name = fields.Char('Marksheet Register', size=256, required=True) @api.constrains('total_pass', 'total_failed') def _check_marks(self): if (self.total_pass < 0.0) or (self.total_failed < 0.0): raise ValidationError('Enter proper pass or fail!')
class OpAttendanceSheet(models.Model): _name = 'op.attendance.sheet' @api.one @api.depends('attendance_line.present') def _total_present(self): self.total_present = len( self.attendance_line.filtered(lambda self: self.present)) @api.one @api.depends('attendance_line.present') def _total_absent(self): self.total_absent = len( self.attendance_line.filtered(lambda self: self.present is False)) name = fields.Char('Name', required=True, size=32) register_id = fields.Many2one('op.attendance.register', 'Register', required=True) course_id = fields.Many2one('op.course', related='register_id.course_id', store=True, readonly=True) batch_id = fields.Many2one('op.batch', 'Batch', related='register_id.batch_id', store=True, readonly=True) attendance_date = fields.Date('Date', required=True, default=lambda self: fields.Date.today()) attendance_line = fields.One2many('op.attendance.line', 'attendance_id', 'Attendance Line') total_present = fields.Integer('Total Present', compute='_total_present') total_absent = fields.Integer('Total Absent', compute='_total_absent') faculty_id = fields.Many2one('op.faculty', 'Faculty')
class hr_recruitment_report(models.Model): _name = "hr.recruitment.report" _description = "Recruitments Statistics" _auto = False _rec_name = 'date_create' _order = 'date_create desc' user_id = fields.Many2one('res.users', 'User', readonly=True) company_id = fields.Many2one('res.company', 'Company', readonly=True) date_create = fields.Datetime('Create Date', readonly=True) date_last_stage_update = fields.Datetime('Last Stage Update', readonly=True) date_closed = fields.Date('Closed', readonly=True) job_id = fields.Many2one('hr.job', 'Applied Job', readonly=True) stage_id = fields.Many2one('hr.recruitment.stage', 'Stage') type_id = fields.Many2one('hr.recruitment.degree', 'Degree') department_id = fields.Many2one('hr.department', 'Department', readonly=True) priority = fields.Selection(hr_recruitment.AVAILABLE_PRIORITIES, 'Appreciation') salary_prop = fields.Float("Salary Proposed", digits=0) salary_prop_avg = fields.Float("Avg. Proposed Salary", group_operator="avg", digits=0) salary_exp = fields.Float("Salary Expected", digits=0) salary_exp_avg = fields.Float("Avg. Expected Salary", group_operator="avg", digits=0) partner_id = fields.Many2one('res.partner', 'Partner', readonly=True) delay_close = fields.Float('Avg. Delay to Close', digits=(16, 2), readonly=True, group_operator="avg", help="Number of Days to close the project issue") last_stage_id = fields.Many2one('hr.recruitment.stage', 'Last Stage') medium_id = fields.Many2one('utm.medium', 'Medium', readonly=True, help="This is the method of delivery. Ex: Postcard, Email, or Banner Ad") source_id = fields.Many2one('utm.source', 'Source', readonly=True, help="This is the source of the link Ex: Search Engine, another domain, or name of email list") def init(self, cr): tools.drop_view_if_exists(cr, 'hr_recruitment_report') cr.execute(""" create or replace view hr_recruitment_report as ( select min(s.id) as id, s.create_date as date_create, date(s.date_closed) as date_closed, s.date_last_stage_update as date_last_stage_update, s.partner_id, s.company_id, s.user_id, s.job_id, s.type_id, s.department_id, s.priority, s.stage_id, s.last_stage_id, s.medium_id, s.source_id, sum(salary_proposed) as salary_prop, (sum(salary_proposed)/count(*)) as salary_prop_avg, sum(salary_expected) as salary_exp, (sum(salary_expected)/count(*)) as salary_exp_avg, extract('epoch' from (s.write_date-s.create_date))/(3600*24) as delay_close, count(*) as nbr from hr_applicant s group by s.date_open, s.create_date, s.write_date, s.date_closed, s.date_last_stage_update, s.partner_id, s.company_id, s.user_id, s.stage_id, s.last_stage_id, s.type_id, s.priority, s.job_id, s.department_id, s.medium_id, s.source_id ) """)
class OpAdmissionRegister(models.Model): _name = 'op.admission.register' _inherit = 'mail.thread' _description = 'Admission Register' name = fields.Char('Name', required=True, readonly=True, states={'draft': [('readonly', False)]}) start_date = fields.Date('Start Date', required=True, readonly=True, default=fields.Date.today(), states={'draft': [('readonly', False)]}) end_date = fields.Date('End Date', required=True, readonly=True, default=(datetime.today() + relativedelta(days=30)), states={'draft': [('readonly', False)]}) course_id = fields.Many2one('op.course', 'Course', required=True, readonly=True, states={'draft': [('readonly', False)]}, track_visibility='onchange') min_count = fields.Integer('Minimum No. of Admission', readonly=True, states={'draft': [('readonly', False)]}) max_count = fields.Integer('Maximum No. of Admission', readonly=True, states={'draft': [('readonly', False)]}, default=30) product_id = fields.Many2one('product.product', 'Product', required=True, domain=[('type', '=', 'service')], readonly=True, states={'draft': [('readonly', False)]}, track_visibility='onchange') admission_ids = fields.One2many('op.admission', 'register_id', 'Admissions') state = fields.Selection([('draft', 'Draft'), ('confirm', 'Confirmed'), ('cancel', 'Cancelled'), ('application', 'Application Gathering'), ('admission', 'Admission Process'), ('done', 'Done')], 'Status', default='draft', track_visibility='onchange') @api.one @api.constrains('start_date', 'end_date') def check_dates(self): start_date = fields.Date.from_string(self.start_date) end_date = fields.Date.from_string(self.end_date) if start_date > end_date: raise ValidationError("End Date cannot be set before Start Date.") @api.one @api.constrains('min_count', 'max_count') def check_no_of_admission(self): if (self.min_count < 0) or (self.max_count < 0): raise ValidationError("No of Admission should be positive!") if self.min_count > self.max_count: raise ValidationError( "Min Admission can't be greater than Max Admission") @api.one def confirm_register(self): self.state = 'confirm' @api.one def set_to_draft(self): self.state = 'draft' @api.one def cancel_register(self): self.state = 'cancel' @api.one def start_application(self): self.state = 'application' @api.one def start_admission(self): self.state = 'admission' @api.one def close_register(self): self.state = 'done'
class Applicant(models.Model): _name = "hr.applicant" _description = "Applicant" _order = "priority desc, id desc" _inherit = ['mail.thread', 'ir.needaction_mixin', 'utm.mixin'] _mail_mass_mailing = _('Applicants') def _default_stage_id(self): if self._context.get('default_job_id'): ids = self.env['hr.recruitment.stage'].search([ ('job_ids', '=', self._context['default_job_id']), ('fold', '=', False) ], order='sequence asc', limit=1).ids if ids: return ids[0] return False def _default_company_id(self): company_id = False if self._context.get('default_department_id'): department = self.env['hr.department'].browse(self._context['default_department_id']) company_id = department.company_id.id if not company_id: company_id = self.env['res.company']._company_default_get('hr.applicant') return company_id name = fields.Char("Subject / Application Name", required=True) active = fields.Boolean("Active", default=True, help="If the active field is set to false, it will allow you to hide the case without removing it.") description = fields.Text("Description") email_from = fields.Char("Email", size=128, help="These people will receive email.") email_cc = fields.Text("Watchers Emails", size=252, 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") probability = fields.Float("Probability") partner_id = fields.Many2one('res.partner', "Contact") create_date = fields.Datetime("Creation Date", readonly=True, select=True) write_date = fields.Datetime("Update Date", readonly=True) stage_id = fields.Many2one('hr.recruitment.stage', 'Stage', track_visibility='onchange', domain="[('job_ids', '=', job_id)]", copy=False, select=1, default=_default_stage_id) last_stage_id = fields.Many2one('hr.recruitment.stage', "Last Stage", help="Stage of the applicant before being in the current stage. Used for lost cases analysis.") categ_ids = fields.Many2many('hr.applicant.category', string="Tags") company_id = fields.Many2one('res.company', "Company", default=_default_company_id) user_id = fields.Many2one('res.users', "Responsible", track_visibility="onchange", default=lambda self: self.env.uid) date_closed = fields.Datetime("Closed", readonly=True, select=True) date_open = fields.Datetime("Assigned", readonly=True, select=True) date_last_stage_update = fields.Datetime("Last Stage Update", select=True, default=fields.Datetime.now) date_action = fields.Date("Next Action Date") title_action = fields.Char("Next Action", size=64) priority = fields.Selection(AVAILABLE_PRIORITIES, "Appreciation", default='0') job_id = fields.Many2one('hr.job', "Applied Job") salary_proposed_extra = fields.Char("Proposed Salary Extra", help="Salary Proposed by the Organisation, extra advantages") salary_expected_extra = fields.Char("Expected Salary Extra", help="Salary Expected by Applicant, extra advantages") salary_proposed = fields.Float("Proposed Salary", help="Salary Proposed by the Organisation") salary_expected = fields.Float("Expected Salary", help="Salary Expected by Applicant") availability = fields.Date("Availability", help="The date at which the applicant will be available to start working") partner_name = fields.Char("Applicant's Name") partner_phone = fields.Char("Phone", size=32) partner_mobile = fields.Char("Mobile", size=32) type_id = fields.Many2one('hr.recruitment.degree', "Degree") department_id = fields.Many2one('hr.department', "Department") survey = fields.Many2one('survey.survey', related='job_id.survey_id', string="Survey") # TDE FIXME: rename to survey_id response_id = fields.Many2one('survey.user_input', "Response", ondelete="set null", oldname="response") reference = fields.Char("Referred By") day_open = fields.Float(compute='_compute_day', string="Days to Open") day_close = fields.Float(compute='_compute_day', string="Days to Close") color = fields.Integer("Color Index", default=0) emp_id = fields.Many2one('hr.employee', string="Employee", track_visibility="onchange", help="Employee linked to the applicant.") user_email = fields.Char(related='user_id.email', type="char", string="User Email", readonly=True) attachment_number = fields.Integer(compute='_get_attachment_number', string="Number of Attachments") employee_name = fields.Char(related='emp_id.name', string="Employee Name") attachment_ids = fields.One2many('ir.attachment', 'res_id', domain=[('res_model', '=', 'hr.applicant')], string='Attachments') @api.depends('date_open', 'date_closed') @api.one def _compute_day(self): if self.date_open: date_create = datetime.strptime(self.create_date, tools.DEFAULT_SERVER_DATETIME_FORMAT) date_open = datetime.strptime(self.date_open, tools.DEFAULT_SERVER_DATETIME_FORMAT) self.day_open = (date_open - date_create).total_seconds() / (24.0 * 3600) if self.date_closed: date_create = datetime.strptime(self.create_date, tools.DEFAULT_SERVER_DATETIME_FORMAT) date_closed = datetime.strptime(self.date_closed, tools.DEFAULT_SERVER_DATETIME_FORMAT) self.day_close = (date_closed - date_create).total_seconds() / (24.0 * 3600) @api.multi def _get_attachment_number(self): read_group_res = self.env['ir.attachment'].read_group( [('res_model', '=', 'hr.applicant'), ('res_id', 'in', self.ids)], ['res_id'], ['res_id']) attach_data = dict((res['res_id'], res['res_id_count']) for res in read_group_res) for record in self: record.attachment_number = attach_data.get(record.id, 0) @api.model def _read_group_stage_ids(self, ids, domain, read_group_order=None, access_rights_uid=None): access_rights_uid = access_rights_uid or self.env.uid Stage = self.env['hr.recruitment.stage'] order = Stage._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 job_id from the context and write the domain: ids + contextual columns (job or default) job_id = self._context.get('default_job_id') department_id = self._context.get('default_department_id') search_domain = [] if job_id: search_domain = [('job_ids', '=', job_id)] if department_id: if search_domain: search_domain = ['|', ('job_ids.department_id', '=', department_id)] + search_domain else: search_domain = [('job_ids.department_id', '=', department_id)] if self.ids: if search_domain: search_domain = ['|', ('id', 'in', self.ids)] + search_domain else: search_domain = [('id', 'in', self.ids)] stage_ids = Stage._search(search_domain, order=order, access_rights_uid=access_rights_uid) stages = Stage.sudo(access_rights_uid).browse(stage_ids) result = stages.name_get() # 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 stages: fold[stage.id] = stage.fold or False return result, fold _group_by_full = { 'stage_id': _read_group_stage_ids } @api.onchange('job_id') def onchange_job_id(self): vals = self._onchange_job_id_internal(self.job_id.id) self.department_id = vals['value']['department_id'] self.user_id = vals['value']['user_id'] self.stage_id = vals['value']['stage_id'] def _onchange_job_id_internal(self, job_id): department_id = False user_id = False stage_id = self.stage_id.id if job_id: job = self.env['hr.job'].browse(job_id) department_id = job.department_id.id user_id = job.user_id.id if not self.stage_id: stage_ids = self.env['hr.recruitment.stage'].search([ ('job_ids', '=', job.id), ('fold', '=', False) ], order='sequence asc', limit=1).ids stage_id = stage_ids[0] if stage_ids else False return {'value': { 'department_id': department_id, 'user_id': user_id, 'stage_id': stage_id }} @api.onchange('partner_id') def onchange_partner_id(self): self.partner_phone = self.partner_id.phone self.partner_mobile = self.partner_id.mobile self.email_from = self.partner_id.email @api.onchange('stage_id') def onchange_stage_id(self): vals = self._onchange_stage_id_internal(self.stage_id.id) if vals['value'].get('date_closed'): self.date_closed = vals['value']['date_closed'] def _onchange_stage_id_internal(self, stage_id): if not stage_id: return {'value': {}} stage = self.env['hr.recruitment.stage'].browse(stage_id) if stage.fold: return {'value': {'date_closed': fields.datetime.now()}} return {'value': {'date_closed': False}} @api.model def create(self, vals): if vals.get('department_id') and not self._context.get('default_department_id'): self = self.with_context(default_department_id=vals.get('department_id')) if vals.get('job_id') or self._context.get('default_job_id'): job_id = vals.get('job_id') or self._context.get('default_job_id') for key, value in self._onchange_job_id_internal(job_id)['value'].iteritems(): if key not in vals: vals[key] = value if vals.get('user_id'): vals['date_open'] = fields.Datetime.now() if 'stage_id' in vals: vals.update(self._onchange_stage_id_internal(vals.get('stage_id'))['value']) return super(Applicant, self.with_context(mail_create_nolog=True)).create(vals) @api.multi def write(self, vals): # user_id change: update date_open if vals.get('user_id'): vals['date_open'] = fields.Datetime.now() # stage_id: track last stage before update if 'stage_id' in vals: vals['date_last_stage_update'] = fields.Datetime.now() vals.update(self._onchange_stage_id_internal(vals.get('stage_id'))['value']) for applicant in self: vals['last_stage_id'] = applicant.stage_id.id res = super(Applicant, self).write(vals) else: res = super(Applicant, self).write(vals) # post processing: if stage changed, post a message in the chatter if vals.get('stage_id'): if self.stage_id.template_id: self.message_post_with_template(self.stage_id.template_id.id, notify=True, composition_mode='mass_mail') return res @api.model def get_empty_list_help(self, help): return super(Applicant, self.with_context(empty_list_help_model='hr.job', empty_list_help_id=self.env.context.get('default_job_id'), empty_list_help_document_name=_("job applicants"))).get_empty_list_help(help) @api.multi def action_get_created_employee(self): self.ensure_one() action = self.env['ir.actions.act_window'].for_xml_id('hr', 'open_view_employee_list') action['res_id'] = self.mapped('emp_id').ids[0] return action @api.multi def action_makeMeeting(self): """ This opens Meeting's calendar view to schedule meeting on current applicant @return: Dictionary value for created Meeting view """ self.ensure_one() partners = self.partner_id | self.user_id.partner_id | self.department_id.manager_id.user_id.partner_id category = self.env.ref('hr_recruitment.categ_meet_interview') res = self.env['ir.actions.act_window'].for_xml_id('calendar', 'action_calendar_event') res['context'] = { 'search_default_partner_ids': self.partner_id.name, 'default_partner_ids': partners.ids, 'default_user_id': self.env.uid, 'default_name': self.name, 'default_categ_ids': category and [category.id] or False, } return res @api.multi def action_start_survey(self): self.ensure_one() # create a response and link it to this applicant if not self.response_id: response = self.env['survey.user_input'].create({'survey_id': self.survey.id, 'partner_id': self.partner_id.id}) self.response_id = response.id else: response = self.response_id # grab the token of the response and start surveying return self.survey.with_context(survey_token=response.token).action_start_survey() @api.multi def action_print_survey(self): """ If response is available then print this response otherwise print survey form (print template of the survey) """ self.ensure_one() if not self.response_id: return self.survey.action_print_survey() else: response = self.response_id return self.survey.with_context(survey_token=response.token).action_print_survey() @api.multi def action_get_attachment_tree_view(self): attachment_action = self.env.ref('base.action_attachment') action = attachment_action.read()[0] action['context'] = {'default_res_model': self._name, 'default_res_id': self.ids[0]} action['domain'] = str(['&', ('res_model', '=', self._name), ('res_id', 'in', self.ids)]) return action @api.multi def _track_subtype(self, init_values): record = self[0] if 'emp_id' in init_values and record.emp_id: return 'hr_recruitment.mt_applicant_hired' elif 'stage_id' in init_values and record.stage_id and record.stage_id.sequence <= 1: return 'hr_recruitment.mt_applicant_new' elif 'stage_id' in init_values and record.stage_id and record.stage_id.sequence > 1: return 'hr_recruitment.mt_applicant_stage_changed' return super(Applicant, self)._track_subtype(init_values) @api.model def message_get_reply_to(self, ids, default=None): """ Override to get the reply_to of the parent project. """ applicants = self.sudo().browse(ids) aliases = self.env['hr.job'].message_get_reply_to(applicants.mapped('job_id').ids, default=default) return dict((applicant.id, aliases.get(applicant.job_id and applicant.job_id.id or 0, False)) for applicant in applicants) @api.multi def message_get_suggested_recipients(self): recipients = super(Applicant, self).message_get_suggested_recipients() for applicant in self: if applicant.partner_id: applicant._message_add_suggested_recipient(recipients, partner=applicant.partner_id, reason=_('Contact')) elif applicant.email_from: applicant._message_add_suggested_recipient(recipients, email=applicant.email_from, reason=_('Contact Email')) return recipients @api.model def message_new(self, msg, custom_values=None): """ Overrides mail_thread message_new that is called by the mailgateway through message_process. This override updates the document according to the email. """ val = msg.get('from').split('<')[0] defaults = { 'name': msg.get('subject') or _("No Subject"), 'partner_name': val, 'email_from': msg.get('from'), 'email_cc': msg.get('cc'), 'user_id': False, 'partner_id': msg.get('author_id', False), } if msg.get('priority'): defaults['priority'] = msg.get('priority') if custom_values: defaults.update(custom_values) return super(Applicant, self).message_new(msg, custom_values=defaults) @api.multi def create_employee_from_applicant(self): """ Create an hr.employee from the hr.applicants """ employee = False for applicant in self: address_id = contact_name = False if applicant.partner_id: address_id = applicant.partner_id.address_get(['contact'])['contact'] contact_name = applicant.partner_id.name_get()[0][1] if applicant.job_id and (applicant.partner_name or contact_name): applicant.job_id.write({'no_of_hired_employee': applicant.job_id.no_of_hired_employee + 1}) employee = self.env['hr.employee'].create({'name': applicant.partner_name or contact_name, 'job_id': applicant.job_id.id, 'address_home_id': address_id, 'department_id': applicant.department_id.id or False, 'address_id': applicant.company_id and applicant.company_id.partner_id and applicant.company_id.partner_id.id or False, 'work_email': applicant.department_id and applicant.department_id.company_id and applicant.department_id.company_id.email or False, 'work_phone': applicant.department_id and applicant.department_id.company_id and applicant.department_id.company_id.phone or False}) applicant.write({'emp_id': employee.id}) applicant.job_id.message_post( body=_('New Employee %s Hired') % applicant.partner_name if applicant.partner_name else applicant.name, subtype="hr_recruitment.mt_job_applicant_hired") employee._broadcast_welcome() else: raise UserError(_('You must define an Applied Job and a Contact Name for this applicant.')) employee_action = self.env.ref('hr.open_view_employee_list') dict_act_window = employee_action.read([])[0] if employee: dict_act_window['res_id'] = employee.id dict_act_window['view_mode'] = 'form,tree' return dict_act_window @api.multi def archive_applicant(self): self.write({'active': False}) @api.multi def reset_applicant(self): """ Reinsert the applicant into the recruitment pipe in the first stage""" for applicant in self: first_stage_obj = self.env['hr.recruitment.stage'].search([('job_ids', 'in', applicant.job_id.id)], order="sequence asc", limit=1) applicant.write({'active': True, 'stage_id': first_stage_obj.id})
class TimeTableReport(models.TransientModel): _name = 'time.table.report' _description = 'Generate Time Table Report' state = fields.Selection([('faculty', 'Faculty'), ('student', 'Student')], string='Select', required=True, default='faculty') course_id = fields.Many2one('op.course', 'Course') batch_id = fields.Many2one('op.batch', 'Batch') faculty_id = fields.Many2one('op.faculty', 'Faculty') start_date = fields.Date( 'Start Date', required=True, default=(datetime.today() - relativedelta(days=datetime.date(datetime.today()).weekday()) ).strftime('%Y-%m-%d')) end_date = fields.Date( 'End Date', required=True, default=( datetime.today() + relativedelta(days=6 - datetime.date(datetime.today()).weekday()) ).strftime('%Y-%m-%d')) @api.one @api.constrains('start_date', 'end_date') def _check_dates(self): start_date = fields.Date.from_string(self.start_date) end_date = fields.Date.from_string(self.end_date) if end_date < start_date: raise ValidationError('End Date cannot be set before Start Date.') elif end_date > (start_date + timedelta(days=6)): raise ValidationError("Select date range for a week!") @api.onchange('course_id') def onchange_course(self): self.batch_id = False @api.multi def gen_time_table_report(self): data = self.read([ 'start_date', 'end_date', 'course_id', 'batch_id', 'state', 'faculty_id' ])[0] if data['state'] == 'student': time_table_ids = self.env['op.timetable'].search( [('course_id', '=', data['course_id'][0]), ('batch_id', '=', data['batch_id'][0]), ('start_datetime', '>', data['start_date'] + '%H:%M:%S'), ('end_datetime', '<', data['end_date'] + '%H:%M:%S')], order='start_datetime asc') data.update({'time_table_ids': time_table_ids.ids}) return self.env['report'].get_action( self, 'openeducat_timetable.report_timetable_student_generate', data=data) else: teacher_time_table_ids = self.env['op.timetable'].search( [('start_datetime', '>', data['start_date'] + '%H:%M:%S'), ('end_datetime', '<', data['end_date'] + '%H:%M:%S'), ('faculty_id', '=', data['faculty_id'][0])], order='start_datetime asc') data.update({'teacher_time_table_ids': teacher_time_table_ids.ids}) return self.env['report'].get_action( self, 'openeducat_timetable.report_timetable_teacher_generate', data=data)
class SaleForecastLoad(models.TransientModel): _name = 'sale.forecast.load' def _get_default_partner(self): model = self.env.context.get('active_model', False) record = self.env[model].browse(self.env.context.get('active_id')) partner = False if model == 'sale.order': partner = record.partner_id return partner def _get_default_forecast(self): model = self.env.context.get('active_model', False) record = self.env[model].browse(self.env.context.get('active_id')) forecast = False if model == 'procurement.sale.forecast': forecast = record.id return forecast def _get_default_sale(self): model = self.env.context.get('active_model', False) record = self.env[model].browse(self.env.context.get('active_id')) sale = False if model == 'sale.order': sale = record.id return sale def _get_default_date_from(self): model = self.env.context.get('active_model', False) record = self.env[model].browse(self.env.context.get('active_id')) date_from = False if model == 'sale.order': date_from = record.date_order elif model == 'procurement.sale.forecast': reg_date = record.date_from cur_year = fields.Date.from_string(reg_date).year date_from = fields.Date.from_string(reg_date).replace( year=cur_year - 1) return date_from def _get_default_date_to(self): model = self.env.context.get('active_model', False) record = self.env[model].browse(self.env.context.get('active_id')) date_to = False if model == 'sale.order': date_to = record.date_order elif model == 'procurement.sale.forecast': reg_date = record.date_to cur_year = fields.Date.from_string(reg_date).year date_to = fields.Date.from_string(reg_date).replace(year=cur_year - 1) return date_to partner_id = fields.Many2one("res.partner", string="Partner", default=_get_default_partner) date_from = fields.Date(string="Date from", default=_get_default_date_from) date_to = fields.Date(string="Date to", default=_get_default_date_to) sale_id = fields.Many2one("sale.order", "Sale", default=_get_default_sale) forecast_id = fields.Many2one("procurement.sale.forecast", "Forecast", default=_get_default_forecast) product_categ_id = fields.Many2one("product.category", string="Category") product_tmpl_id = fields.Many2one("product.template", string="Template") product_id = fields.Many2one("product.product", string="Product") factor = fields.Float(string="Factor", default=1) @api.onchange('sale_id') def sale_onchange(self): if self.sale_id: self.partner_id = self.sale_id.partner_id.id self.date_from = self.sale_id.date_order self.date_to = self.sale_id.date_order @api.onchange('forecast_id') def forecast_onchange(self): if self.forecast_id: from_date = self.forecast_id.date_from to_date = self.forecast_id.date_to f_cur_year = fields.Date.from_string(from_date).year t_cur_year = fields.Date.from_string(to_date).year date_from = fields.Date.from_string(from_date).replace( year=f_cur_year - 1) date_to = fields.Date.from_string(to_date).replace( year=t_cur_year - 1) self.date_from = date_from self.date_to = date_to @api.multi def match_sales_forecast(self, sales, factor): self.ensure_one() forecast_line_obj = self.env['procurement.sale.forecast.line'] res = {} for sale in sales: forecast = self.forecast_id.id product = sale.product_id.id partner = self.partner_id.id forecast_lines = forecast_line_obj.search([ ('product_id', '=', product), ('partner_id', '=', partner), ('forecast_id', '=', forecast) ]) if not forecast_lines: if partner not in res: res[partner] = {} if product not in res[partner]: res[partner][product] = {'qty': 0.0, 'amount': 0.0} product_dict = res[partner][product] sum_qty = product_dict['qty'] + sale.product_uom_qty sum_subtotal = (product_dict['amount'] + sale.price_subtotal) product_dict['qty'] = sum_qty * factor product_dict['amount'] = sum_subtotal return res @api.multi def get_date_list(self, forecast): self.ensure_one() date_list = [] date_start = fields.Date.from_string(forecast.date_from) date_end = fields.Date.from_string(forecast.date_to) month_count = ((date_end.year - date_start.year) * 12 + date_end.month - date_start.month) date = '-'.join([str(date_start.year), str(date_start.month), str(1)]) first_date = fields.Date.from_string(date) date_list.append(date) while month_count > 0: next_date = first_date + relativedelta(months=month_count) date_list.append(fields.Date.to_string(next_date)) month_count -= 1 return date_list @api.multi def get_sale_forecast_lists(self, forecast): sale_line_obj = self.env['sale.order.line'] sale_obj = self.env['sale.order'] product_obj = self.env['product.product'] self.ensure_one() sales = [] if self.sale_id: sales = self.sale_id else: sale_domain = [('date_order', '>=', self.date_from), ('date_order', '<=', self.date_to)] if self.partner_id: sale_domain += [('partner_id', '=', self.partner_id.id)] sales = sale_obj.search(sale_domain) sale_line_domain = [('order_id', 'in', sales.ids)] if self.product_id: sale_line_domain += [('product_id', '=', self.product_id.id)] elif self.product_tmpl_id: sale_line_domain += [('product_tmpl_id', '=', self.product_tmpl_id.id)] elif self.product_categ_id: products = product_obj.search([('categ_id', '=', self.product_categ_id.id)]) sale_line_domain += [('product_id', 'in', products.ids)] sale_lines = sale_line_obj.search(sale_line_domain) return sale_lines @api.multi def load_sales(self): self.ensure_one() forecast_line_obj = self.env['procurement.sale.forecast.line'] forecast = self.forecast_id sale_lines = self.get_sale_forecast_lists(forecast) date_list = self.get_date_list(forecast) date_start = fields.Date.from_string(self.date_from) date_end = fields.Date.from_string(self.date_to) month_count = ((date_end.year - date_start.year) * 12 + date_end.month - date_start.month + 1) result = self.match_sales_forecast(sale_lines, self.factor) for date in date_list: for partner in result.keys(): for product in result[partner].keys(): prod_vals = result[partner][product] forecast_line_vals = { 'product_id': product, 'forecast_id': self.forecast_id.id, 'partner_id': partner, 'date': date, 'qty': (prod_vals['qty'] / month_count), 'unit_price': (prod_vals['amount'] / prod_vals['qty']) } forecast_line_obj.create(forecast_line_vals) return True
class report_stock_forecast(models.Model): _name = 'report.stock.forecast' _auto = False date = fields.Date(string='Date') product_id = fields.Many2one('product.product', string='Product', readonly=True) product_tmpl_id = fields.Many2one('product.template', string='Product', related='product_id.product_tmpl_id', readonly=True) cumulative_quantity = fields.Float(string='Cumulative Quantity', readonly=True) quantity = fields.Float(readonly=True) def init(self, cr): tools.drop_view_if_exists(cr, 'report_stock_forecast') cr.execute("""CREATE or REPLACE VIEW report_stock_forecast AS (SELECT MIN(id) as id, product_id as product_id, date as date, sum(product_qty) AS quantity, sum(sum(product_qty)) OVER (PARTITION BY product_id ORDER BY date) AS cumulative_quantity FROM (SELECT MIN(id) as id, MAIN.product_id as product_id, SUB.date as date, CASE WHEN MAIN.date = SUB.date THEN sum(MAIN.product_qty) ELSE 0 END as product_qty FROM (SELECT MIN(sq.id) as id, sq.product_id, date_trunc('week', to_date(to_char(CURRENT_DATE, 'YYYY/MM/DD'), 'YYYY/MM/DD')) as date, SUM(sq.qty) AS product_qty FROM stock_quant as sq LEFT JOIN product_product ON product_product.id = sq.product_id LEFT JOIN stock_location location_id ON sq.location_id = location_id.id WHERE location_id.usage = 'internal' GROUP BY date, sq.product_id UNION ALL SELECT MIN(-sm.id) as id, sm.product_id, CASE WHEN sm.date_expected > CURRENT_DATE THEN date_trunc('week', to_date(to_char(sm.date_expected, 'YYYY/MM/DD'), 'YYYY/MM/DD')) ELSE date_trunc('week', to_date(to_char(CURRENT_DATE, 'YYYY/MM/DD'), 'YYYY/MM/DD')) END AS date, SUM(sm.product_qty) AS product_qty FROM stock_move as sm LEFT JOIN product_product ON product_product.id = sm.product_id LEFT JOIN stock_location dest_location ON sm.location_dest_id = dest_location.id LEFT JOIN stock_location source_location ON sm.location_id = source_location.id WHERE sm.state IN ('confirmed','assigned','waiting') and source_location.usage != 'internal' and dest_location.usage = 'internal' GROUP BY sm.date_expected,sm.product_id UNION ALL SELECT MIN(-sm.id) as id, sm.product_id, CASE WHEN sm.date_expected > CURRENT_DATE THEN date_trunc('week', to_date(to_char(sm.date_expected, 'YYYY/MM/DD'), 'YYYY/MM/DD')) ELSE date_trunc('week', to_date(to_char(CURRENT_DATE, 'YYYY/MM/DD'), 'YYYY/MM/DD')) END AS date, SUM(-(sm.product_qty)) AS product_qty FROM stock_move as sm LEFT JOIN product_product ON product_product.id = sm.product_id LEFT JOIN stock_location source_location ON sm.location_id = source_location.id LEFT JOIN stock_location dest_location ON sm.location_dest_id = dest_location.id WHERE sm.state IN ('confirmed','assigned','waiting') and source_location.usage = 'internal' and dest_location.usage != 'internal' GROUP BY sm.date_expected,sm.product_id) as MAIN LEFT JOIN (SELECT DISTINCT date FROM ( SELECT date_trunc('week', CURRENT_DATE) AS DATE UNION ALL SELECT date_trunc('week', to_date(to_char(sm.date_expected, 'YYYY/MM/DD'), 'YYYY/MM/DD')) AS date FROM stock_move sm LEFT JOIN stock_location source_location ON sm.location_id = source_location.id LEFT JOIN stock_location dest_location ON sm.location_dest_id = dest_location.id WHERE sm.state IN ('confirmed','assigned','waiting') and sm.date_expected > CURRENT_DATE and ((dest_location.usage = 'internal' AND source_location.usage != 'internal') or (source_location.usage = 'internal' AND dest_location.usage != 'internal'))) AS DATE_SEARCH) SUB ON (SUB.date IS NOT NULL) GROUP BY MAIN.product_id,SUB.date, MAIN.date ) AS FINAL GROUP BY product_id,date)""")
class ProcurementSaleForecast(models.Model): _name = 'procurement.sale.forecast' @api.one @api.depends('forecast_lines.procurement_id') def _get_procurement_count(self): procurement_lst = [] for line in self.forecast_lines: if line.procurement_id: procurement_lst.append(line.procurement_id.id) self.procurement_count = len(procurement_lst) name = fields.Char(string='Name', required=True) date_from = fields.Date(string='Date From', required=True) date_to = fields.Date(string='Date To', required=True) forecast_lines = fields.One2many('procurement.sale.forecast.line', 'forecast_id', string="Forecast Lines") warehouse_id = fields.Many2one('stock.warehouse', string="Warehouse") procurement_count = fields.Integer(string="Procurement Count", compute=_get_procurement_count) @api.one @api.constrains('date_from', 'date_to') def check_dates(self): if self.date_from >= self.date_to: raise exceptions.Warning( _('Error! Date to must be lower ' 'than date from.')) @api.multi def create_procurements(self): procurement_obj = self.env['procurement.order'] procure_lst = [] for record in self: for product_line in record.forecast_lines: if product_line.product_id and not product_line.procurement_id: procure_id = procurement_obj.create({ 'name': ('MPS: ' + record.name + ' (' + record.date_from + '.' + record.date_to + ') ' + record.warehouse_id.name), 'date_planned': product_line.date, 'product_id': product_line.product_id.id, 'product_qty': product_line.qty, 'product_uom': product_line.product_id.uom_id.id, 'location_id': record.warehouse_id.lot_stock_id.id, 'company_id': record.warehouse_id.company_id.id, 'warehouse_id': record.warehouse_id.id, }) procure_id.signal_workflow('button_confirm') procure_lst.append(procure_id.id) product_line.procurement_id = procure_id.id return { 'view_type': 'form', 'view_mode': 'tree,form', 'res_model': 'procurement.order', 'res_ids': procure_lst, 'domain': [('id', 'in', procure_lst)], 'type': 'ir.actions.act_window', } @api.multi def show_all_forecast_procurements(self): procurement_list = [] for record in self: for line in record.forecast_lines: if line.procurement_id: procurement_list.append(line.procurement_id.id) return { 'view_type': 'form', 'view_mode': 'tree,form', 'res_model': 'procurement.order', 'res_ids': procurement_list, 'domain': [('id', 'in', procurement_list)], 'type': 'ir.actions.act_window', }