class StaffList(ModelSQL, ModelView): 'Staff list' _name = 'ekd.company.staff_list' _description = __doc__ company = fields.Many2One('company.company', 'Company', required=True) staff = fields.Many2One('ekd.company.staff', 'Staff list') department = fields.Many2One('ekd.company.department', 'Department') name = fields.Char('Name', size=None, required=True) job_function = fields.Many2One('ekd.company.job_function', 'Job function') category = fields.Many2One('ekd.company.employee.category', 'Category') start_date = fields.Date('Start Date') end_date = fields.Date('End Date') regular = fields.Boolean('Regular appointment') free_rate = fields.Function(fields.Float('Free of bets', digits=(3, 3)), 'get_free_rate') #schedule = fields.Many2One('ekd.timesheet.schedule', 'Schedule') payroll = fields.Selection([('hourly', 'Hourly rates'), ('dayly', 'Daily rate'), ('monthly', 'Monthly salary')], 'Accounting payroll') rate_payroll = fields.Function( fields.Numeric('Rate payroll', digits=(16, 2)), 'get_rate_payroll') time_management = fields.Selection([('in_day', 'In Day'), ('in_hour', 'In Hour')], 'Time management') work_rate = fields.Float('Number of bets', digits=(3, 3)) personal = fields.One2Many('ekd.company.employee.account', 'staff_list', 'Employee') payroll = fields.One2Many('ekd.company.staff_list.payroll', 'staff_list', 'Payroll') active = fields.Boolean('Active') note = fields.Text('Note') state = fields.Selection([ ('draft', 'Draft'), ('active', 'Active'), ('archive', 'Archive'), ], 'State', readonly=True, required=True, select=1) #def __init__(self): # super(StaffList, self).__init__() def default_company(self): return Transaction().context.get('company') or False def get_free_rate(self, ids, name): res = {}.fromkeys(ids, 0.0) for line in self.browse(ids): res[line.id] = line.work_rate for employee in line.personal: res[line.id] -= employee.work_rate def get_rate_payroll(self, ids, name): res = {}.fromkeys(ids, Decimal('0.0')) for line in self.browse(ids): for rate_payroll in line.payroll: res[line.id] += rate_payroll.rate
class Date(ModelSQL): 'Date' __name__ = 'test.date' date = fields.Date(string='Date', help='Test date', required=False)
class Work(sequence_ordered(), tree(separator='\\'), ModelSQL, ModelView): 'Work Effort' __name__ = 'project.work' name = fields.Char('Name', required=True, select=True) type = fields.Selection([('project', 'Project'), ('task', 'Task')], 'Type', required=True, select=True) company = fields.Many2One('company.company', 'Company', required=True, select=True) party = fields.Many2One('party.party', 'Party', states={ 'invisible': Eval('type') != 'project', }, depends=['type']) party_address = fields.Many2One('party.address', 'Contact Address', domain=[('party', '=', Eval('party'))], states={ 'invisible': Eval('type') != 'project', }, depends=['party', 'type']) timesheet_works = fields.One2Many('timesheet.work', 'origin', 'Timesheet Works', readonly=True, size=1) timesheet_available = fields.Function( fields.Boolean('Available on timesheets'), 'get_timesheet_available', setter='set_timesheet_available') timesheet_start_date = fields.Function(fields.Date( 'Timesheet Start', states={ 'invisible': ~Eval('timesheet_available'), }, depends=['timesheet_available']), 'get_timesheet_date', setter='set_timesheet_date') timesheet_end_date = fields.Function(fields.Date( 'Timesheet End', states={ 'invisible': ~Eval('timesheet_available'), }, depends=['timesheet_available']), 'get_timesheet_date', setter='set_timesheet_date') timesheet_duration = fields.Function( fields.TimeDelta( 'Duration', 'company_work_time', help="Total time spent on this work and the sub-works."), 'get_total') effort_duration = fields.TimeDelta('Effort', 'company_work_time', help="Estimated Effort for this work.") total_effort = fields.Function( fields.TimeDelta( 'Total Effort', 'company_work_time', help="Estimated total effort for this work and the sub-works."), 'get_total') progress = fields.Float('Progress', domain=[ 'OR', ('progress', '=', None), [ ('progress', '>=', 0), ('progress', '<=', 1), ], ], help='Estimated progress for this work.') total_progress = fields.Function( fields.Float( 'Total Progress', digits=(16, 4), help='Estimated total progress for this work and the sub-works.', states={ 'invisible': Eval('total_progress', None) == None, }), 'get_total') comment = fields.Text('Comment') parent = fields.Many2One('project.work', 'Parent', left='left', right='right', ondelete='RESTRICT', domain=[ ('company', '=', Eval('company', -1)), ], depends=['company']) left = fields.Integer('Left', required=True, select=True) right = fields.Integer('Right', required=True, select=True) children = fields.One2Many('project.work', 'parent', 'Children', domain=[ ('company', '=', Eval('company', -1)), ], depends=['company']) state = fields.Selection([ ('opened', 'Opened'), ('done', 'Done'), ], 'State', required=True, select=True) @staticmethod def default_type(): return 'task' @classmethod def default_company(cls): return Transaction().context.get('company') @staticmethod def default_state(): return 'opened' @classmethod def default_left(cls): return 0 @classmethod def default_right(cls): return 0 @classmethod def __register__(cls, module_name): TimesheetWork = Pool().get('timesheet.work') cursor = Transaction().connection.cursor() table_project_work = cls.__table_handler__(module_name) project = cls.__table__() timesheet = TimesheetWork.__table__() work_exist = table_project_work.column_exist('work') add_parent = (not table_project_work.column_exist('parent') and work_exist) add_company = (not table_project_work.column_exist('company') and work_exist) add_name = (not table_project_work.column_exist('name') and work_exist) super(Work, cls).__register__(module_name) # Migration from 3.4: change effort into timedelta effort_duration if table_project_work.column_exist('effort'): cursor.execute(*project.select( project.id, project.effort, where=project.effort != Null)) for id_, effort in cursor.fetchall(): duration = datetime.timedelta(hours=effort) cursor.execute( *project.update([project.effort_duration], [duration], where=project.id == id_)) table_project_work.drop_column('effort') # Migration from 3.6: add parent, company, drop required on work, # fill name if add_parent: second_project = cls.__table__() query = project.join( timesheet, condition=project.work == timesheet.id).join( second_project, condition=timesheet.parent == second_project.work).select( project.id, second_project.id) cursor.execute(*query) for id_, parent in cursor.fetchall(): cursor.execute(*project.update([project.parent], [parent], where=project.id == id_)) cls._rebuild_tree('parent', None, 0) if add_company: cursor.execute(*project.join( timesheet, condition=project.work == timesheet.id).select( project.id, timesheet.company)) for id_, company in cursor.fetchall(): cursor.execute(*project.update([project.company], [company], where=project.id == id_)) table_project_work.not_null_action('work', action='remove') if add_name: cursor.execute(*project.join( timesheet, condition=project.work == timesheet.id).select( project.id, timesheet.name)) for id_, name in cursor.fetchall(): cursor.execute(*project.update([project.name], [name], where=project.id == id_)) # Migration from 4.0: remove work if work_exist: table_project_work.drop_constraint('work_uniq') update = Transaction().connection.cursor() cursor.execute(*project.select( project.id, project.work, where=project.work != Null)) for project_id, work_id in cursor: update.execute(*timesheet.update( [timesheet.origin, timesheet.name], ['%s,%s' % (cls.__name__, project_id), Null], where=timesheet.id == work_id)) table_project_work.drop_column('work') @classmethod def index_set_field(cls, name): index = super(Work, cls).index_set_field(name) if name in {'timesheet_start_date', 'timesheet_end_date'}: index = cls.index_set_field('timesheet_available') + 1 return index @classmethod def validate(cls, works): super(Work, cls).validate(works) for work in works: work.check_state() def check_state(self): if (self.state == 'opened' and (self.parent and self.parent.state == 'done')): raise WorkValidationError( gettext('project.msg_work_invalid_parent_state', child=self.rec_name, parent=self.parent.rec_name)) if self.state == 'done': for child in self.children: if child.state == 'opened': raise WorkValidationError( gettext('project.msg_work_invalid_children_state', parent=self.rec_name, child=child.rec_name)) @property def effort_hours(self): if not self.effort_duration: return 0 return self.effort_duration.total_seconds() / 60 / 60 @property def total_effort_hours(self): if not self.total_effort: return 0 return self.total_effort.total_seconds() / 60 / 60 @property def timesheet_duration_hours(self): if not self.timesheet_duration: return 0 return self.timesheet_duration.total_seconds() / 60 / 60 @classmethod def default_timesheet_available(cls): return False def get_timesheet_available(self, name): return bool(self.timesheet_works) @classmethod def set_timesheet_available(cls, projects, name, value): pool = Pool() Timesheet = pool.get('timesheet.work') to_create = [] to_delete = [] for project in projects: if not project.timesheet_works and value: to_create.append({ 'origin': str(project), 'company': project.company.id, }) elif project.timesheet_works and not value: to_delete.extend(project.timesheet_works) if to_create: Timesheet.create(to_create) if to_delete: Timesheet.delete(to_delete) def get_timesheet_date(self, name): if self.timesheet_works: func = { 'timesheet_start_date': min, 'timesheet_end_date': max, }[name] return func(getattr(w, name) for w in self.timesheet_works) @classmethod def set_timesheet_date(cls, projects, name, value): pool = Pool() Timesheet = pool.get('timesheet.work') timesheets = [w for p in projects for w in p.timesheet_works] if timesheets: Timesheet.write(timesheets, { name: value, }) @classmethod def sum_tree(cls, works, values, parents): result = values.copy() works = set((w.id for w in works)) leafs = works - set(parents.values()) while leafs: for work in leafs: works.remove(work) parent = parents.get(work) if parent in result: result[parent] += result[work] next_leafs = set(works) for work in works: parent = parents.get(work) if not parent: continue if parent in next_leafs and parent in works: next_leafs.remove(parent) leafs = next_leafs return result @classmethod def get_total(cls, works, names): cursor = Transaction().connection.cursor() table = cls.__table__() works = cls.search([ ('parent', 'child_of', [w.id for w in works]), ]) work_ids = [w.id for w in works] parents = {} for sub_ids in grouped_slice(work_ids): where = reduce_ids(table.id, sub_ids) cursor.execute(*table.select(table.id, table.parent, where=where)) parents.update(cursor.fetchall()) if 'total_progress' in names and 'total_effort' not in names: names = list(names) names.append('total_effort') result = {} for name in names: values = getattr(cls, '_get_%s' % name)(works) result[name] = cls.sum_tree(works, values, parents) if 'total_progress' in names: digits = cls.total_progress.digits[1] total_progress = result['total_progress'] total_effort = result['total_effort'] for work in works: if total_effort[work.id]: total_progress[work.id] = round( total_progress[work.id] / (total_effort[work.id].total_seconds() / 60 / 60), digits) else: total_effort[work.id] = None return result @classmethod def _get_total_effort(cls, works): return {w.id: w.effort_duration or datetime.timedelta() for w in works} @classmethod def _get_timesheet_duration(cls, works): durations = {} for work in works: value = datetime.timedelta() for timesheet_work in work.timesheet_works: if timesheet_work.duration: value += timesheet_work.duration durations[work.id] = value return durations @classmethod def _get_total_progress(cls, works): return {w.id: w.effort_hours * (w.progress or 0) for w in works} @classmethod def copy(cls, project_works, default=None): if default is None: default = {} else: default = default.copy() default.setdefault('children', None) return super().copy(project_works, default=default) @classmethod def delete(cls, project_works): TimesheetWork = Pool().get('timesheet.work') # Get the timesheet works linked to the project works timesheet_works = [ w for pw in project_works for w in pw.timesheet_works ] super(Work, cls).delete(project_works) if timesheet_works: with Transaction().set_context(_check_access=False): TimesheetWork.delete(timesheet_works) @classmethod def search_global(cls, text): for record, rec_name, icon in super(Work, cls).search_global(text): icon = icon or 'tryton-project' yield record, rec_name, icon
class Paciente(Persona): 'Paciente' _name = 'cefiro.paciente' _description = __doc__ sexo = fields.Selection([('M', 'Masculino'), ('F', 'Femenino'), ('I', u'No contesta/Otro')], 'Sexo') nacimiento = fields.Date('Fecha de Nacimiento') edad = fields.Function( fields.Char('Edad'), 'get_edad' ) #fields.Integer('Edad') #Habría que relacionarlo con la fecha de nacimiento y hacerlo de sólo lectura telefono = fields.Char( u'Teléfono fijo' ) #Char por si hay telefonos internacionales, u otros símbolos celular = fields.Char( u'Teléfono celular') #Char por si hay códigos que no sean números # lista = fields.Many2One('cefiro.lista', 'Lista de espera') # convenioSAPPA = fields.Selection([('f', 'Funcionario'), ('c', u'Cónyuge'), ('p', u'Padre/Madre'), ('h', u'Hijo/a')], u'Relación para el convenio') lugarTrabajo = fields.Char('Lugar de trabajo') funcionario = fields.Char( u'Número de funcionario') #Lo pongo char por si hay letras # atencionMedica = fields.Selection([('msp', u'MSP/ASSE'), ('mut', 'Mutualista')], u'Tipo de Atención Médica') mutualista = fields.Char('Nombre de la Mutualista') # fechaIngresoExpediente = fields.Date('Fecha de ingreso del expediente') motivo = fields.Text('Motivo de Consulta') observaciones = fields.Text('Observaciones') # horarioPref = fields.Char('Horario de Preferencia') psicologo = fields.Many2One('cefiro.psicologo', u'Psicólogo') consultas = fields.Many2Many('cefiro.encuentro', 'persona', 'evento', 'Consultas') # #Formularios entregados para el SAPPA form_OQ45T1 = fields.Boolean(u'OQ45-T1') form_OQ45T2 = fields.Boolean(u'OQ45-T2') form_EncuestaSatisfaccion = fields.Boolean(u'Encuesta de Satisfacción') form_EcuestaSatPExtProfesional = fields.Boolean( u'Encuesta de Satisfacción - Prof. Externo : Profesional') form_EncuestaSatPExtPaciente = fields.Boolean( u'Encuesta de Satisfacción - Prof. Externo: Paciente') # profExternoDerivacion = fields.Boolean('Derivado a profesional externo') profExternoNombre = fields.Char('Nombre del profesional externo') profExternoFecha = fields.Date(u'Fecha de derivación') #Cálculo de la edad def get_edad(self, ids, name): #usu = User() #usu.create([('name','pruebalala'),('login','loolololo')]) ahora = date.today() res = {} for pac in self.browse(ids): edadtemp = relativedelta(ahora, pac.nacimiento) res[pac.id] = str(edadtemp.years) + u' años' return res
class AparRepresentationForm(Workflow, ModelSQL, ModelView): 'APAR Representation Form' __name__ = 'apar.representation' mail = fields.Boolean('Mail') representation_id = fields.Char('Representation Number', readonly=True) raised_by = fields.Many2One('company.employee', 'Employee', readonly=True) subject = fields.Char( 'Subject', states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state'], ) text = fields.Text( 'Details', states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state'], ) raised_on = fields.Date('Raised On', readonly=True) state = fields.Selection( [('draft', 'Draft'), ('reporting_officer', 'Reporting Officer'), ('reviewing_officer', 'Reviewing Officer'), ('hod', 'HOD'), ('accepting_authority', 'Accepting Authority'), ('acr_cell', 'ACR Cell'), ('submitted_to_acr_cell', 'Submitted to ACR Cell'), ('director', 'Director'), ('closed', 'Closed')], 'Status', readonly=True) comments_reporting = fields.Text( 'Comments by Reporting Officer', states={ 'readonly': ~Eval('state').in_(['reporting_officer']), }, depends=['state'], ) comments_reviewing = fields.Text( 'Comments by Reviewing Officer', states={ 'readonly': ~Eval('state').in_(['reviewing_officer']), }, depends=['state'], ) comments_accepting = fields.Text( 'Comments by Accepting Authority', states={ 'readonly': ~Eval('state').in_(['accepting_authority']), }, depends=['state'], ) comments_hod = fields.Text( 'Comments by HoD', states={ 'readonly': ~Eval('state').in_(['hod']), }, depends=['state'], ) comments_director = fields.Text( 'Comments by Director', states={ 'readonly': ~Eval('state').in_(['director']), }, depends=['state'], ) present_score = fields.Integer('Present Score') score = fields.Integer('Final Score') signatures = fields.One2Many('apar.representation.signatures', 'representation', 'Signatures') @classmethod def __setup__(cls): super().__setup__() cls._buttons.update({ "submit": { 'invisible': ~Eval('state').in_(['draft']), 'depends': ['state'] }, "forward_to_reporting": { 'invisible': ~Eval('state').in_(['submitted_to_acr_cell']), 'depends': ['state'] }, "forward_to_reviewing": { 'invisible': ~Eval('state').in_(['reporting_officer']), 'depends': ['state'] }, "forward_to_accepting": { 'invisible': ~Eval('state').in_(['reviewing_officer']), 'depends': ['state'] }, "forward_to_HoD": { 'invisible': ~Eval('state').in_(['accepting_authority']), 'depends': ['state'] }, "forward_to_acr": { 'invisible': ~Eval('state').in_(['hod']), 'depends': ['state'] }, "forward_to_director": { 'invisible': ~Eval('state').in_(['acr_cell']), 'depends': ['state'] }, "close": { 'invisible': ~Eval('state').in_(['director']), 'depends': ['state'] } }) cls._transitions |= set(( ('draft', 'submitted_to_acr_cell'), ('submitted_to_acr_cell', 'reporting_officer'), ('reporting_officer', 'reviewing_officer'), ('reviewing_officer', 'accepting_authority'), ('accepting_authority', 'hod'), ('hod', 'acr_cell'), ('acr_cell', 'director'), ('director', 'closed'), ('closed', 'closed'), )) @staticmethod def default_state(): return 'draft' def form_signature(self): pool = Pool() sign_obj = pool.get('apar.representation.signatures') User = pool.get('res.user') user = User(Transaction().user) employee = user.employee place = 'Delhi' vals = { 'signed_by_user': user.id, 'signed_by_employee': employee.id if employee else None, 'designation': employee.designation.id if employee else None, 'signed_on': pool.get('ir.date').today(), 'place': place, 'representation': self.id } sign_obj.create([vals]) @classmethod @ModelView.button @Workflow.transition('submitted_to_acr_cell') def submit(self, records): for record in records: record.form_signature() @classmethod @ModelView.button @Workflow.transition('reporting_officer') def forward_to_reporting(self, records): for record in records: record.form_signature() @classmethod @ModelView.button @Workflow.transition('reviewing_officer') def forward_to_reviewing(self, records): for record in records: record.form_signature() @classmethod @ModelView.button @Workflow.transition('accepting_authority') def forward_to_accepting(self, records): for record in records: record.form_signature() @classmethod @ModelView.button @Workflow.transition('hod') def forward_to_HoD(self, records): for record in records: record.form_signature() @classmethod @ModelView.button @Workflow.transition('acr_cell') def forward_to_acr(self, records): for record in records: record.form_signature() @classmethod @ModelView.button @Workflow.transition('director') def forward_to_director(self, records): # TODO: Confirm from the user # if he is sure that he wants to sign the document. for record in records: record.form_signature() @classmethod @ModelView.button @Workflow.transition('closed') def close(self, records): for record in records: record.form_signature()
class QuotationLine(ModelSQL, ModelView): "Purchase Request For Quotation Line" __name__ = 'purchase.request.quotation.line' supplier = fields.Function(fields.Many2One('party.party', 'Supplier'), 'get_supplier') supply_date = fields.Date('Supply Date', help="When it should be delivered.") product = fields.Function(fields.Many2One('product.product', 'Product'), 'get_product', searcher='search_product') description = fields.Text('Description', states={'required': ~Eval('product')}, depends=['product']) quantity = fields.Float('Quantity', digits=(16, Eval('unit_digits', 2)), required=True, depends=['unit_digits']) unit = fields.Many2One( 'product.uom', 'Unit', ondelete='RESTRICT', states={ 'required': Bool(Eval('product')), }, domain=[ If(Bool(Eval('product_uom_category')), ('category', '=', Eval('product_uom_category')), ('category', '!=', -1)), ], depends=['product', 'product_uom_category']) unit_digits = fields.Function(fields.Integer('Unit Digits'), 'on_change_with_unit_digits') product_uom_category = fields.Function( fields.Many2One('product.uom.category', 'Product Uom Category'), 'on_change_with_product_uom_category') unit_price = fields.Numeric('Unit Price', digits=price_digits) currency = fields.Many2One('currency.currency', 'Currency', states={ 'required': Bool(Eval('unit_price')), }, depends=['unit_price']) currency_digits = fields.Function(fields.Integer('Currency Digits'), 'on_change_with_currency_digits') request = fields.Many2One( 'purchase.request', 'Request', ondelete='CASCADE', select=True, required=True, domain=[ If( Eval('quotation_state') == 'draft', ('state', 'in', ['draft', 'quotation', 'received']), (), ), ], states={'readonly': Eval('quotation_state') != 'draft'}, depends=['quotation_state'], help="The request which this line belongs to.") quotation = fields.Many2One('purchase.request.quotation', 'Quotation', ondelete='CASCADE', required=True, domain=[ ('supplier', '=', Eval('supplier')), ], depends=['supplier']) quotation_state = fields.Function(fields.Selection('get_quotation_state', 'Quotation State'), 'on_change_with_quotation_state', searcher='search_quotation_state') @staticmethod def order_quotation_state(tables): pool = Pool() Quotation = pool.get('purchase.request.quotation') quotation_line, _ = tables[None] quotation = Quotation.__table__() tables['purchase.request.quotation'] = { None: (quotation, quotation_line.quotation == quotation.id), } return [ Case((quotation.state == 'received', 0), else_=1), quotation.state ] def get_supplier(self, name): if self.quotation and self.quotation.supplier: return self.quotation.supplier.id @fields.depends('request', '_parent_request.product', '_parent_request.description', '_parent_request.quantity', '_parent_request.uom', '_parent_request.company.currency', '_parent_request.supply_date') def on_change_request(self): if self.request: self.product = self.request.product self.description = self.request.description self.quantity = self.request.quantity self.unit = self.request.uom if self.request.company: self.currency = self.request.company.currency self.supply_date = self.request.supply_date or datetime.date.max @fields.depends('unit') def on_change_with_unit_digits(self, name=None): if self.unit: return self.unit.digits return None @fields.depends('product') def on_change_with_product_uom_category(self, name=None): if self.product: return self.product.default_uom_category.id @fields.depends('currency') def on_change_with_currency_digits(self, name=None): if self.currency: return self.currency.digits return None @classmethod def get_quotation_state(cls): pool = Pool() Quotation = pool.get('purchase.request.quotation') return (Quotation.fields_get(['state'])['state']['selection']) @fields.depends('quotation', '_parent_quotation.state') def on_change_with_quotation_state(self, name=None): pool = Pool() Quotation = pool.get('purchase.request.quotation') if self.quotation: return self.quotation.state return Quotation.default_state() @classmethod def search_quotation_state(cls, name, clause): return [('quotation.state', ) + tuple(clause[1:])] def get_rec_name(self, name): return '%s - %s' % (self.quotation.rec_name, self.supplier.rec_name) @classmethod def search_rec_name(cls, name, clause): names = clause[2].split(' - ', 1) res = [('quotation', clause[1], names[0])] if len(names) != 1 and names[1]: res.append(('supplier', clause[1], names[1])) return res @classmethod def delete(cls, quotationlines): pool = Pool() Request = pool.get('purchase.request') requests = [l.request for l in quotationlines] super(QuotationLine, cls).delete(quotationlines) Request.update_state(requests) def get_product(self, name): if self.request and self.request.product: return self.request.product.id @classmethod def search_product(cls, name, clause): return [('request.' + clause[0], ) + tuple(clause[1:])]
class Period(Workflow, ModelSQL, ModelView): 'Stock Period' __name__ = 'stock.period' date = fields.Date('Date', required=True, states={ 'readonly': Eval('state') == 'closed', }, depends=['state']) company = fields.Many2One('company.company', 'Company', required=True, domain=[ ('id', If(Eval('context', {}).contains('company'), '=', '!='), Eval('context', {}).get('company', -1)), ]) caches = fields.One2Many('stock.period.cache', 'period', 'Caches', readonly=True) state = fields.Selection([ ('draft', 'Draft'), ('closed', 'Closed'), ], 'State', select=True, readonly=True) @classmethod def __setup__(cls): super(Period, cls).__setup__() cls._error_messages.update({ 'close_period_future_today': ('You can not close a period ' 'in the future or today.'), 'close_period_assigned_move': ( 'You can not close a period when ' 'there still are assigned moves.'), }) cls._transitions |= set(( ('draft', 'closed'), ('closed', 'draft'), )) cls._buttons.update({ 'draft': { 'invisible': Eval('state') == 'draft', 'depends': ['state'], }, 'close': { 'invisible': Eval('state') == 'closed', 'depends': ['state'], }, }) @staticmethod def default_company(): return Transaction().context.get('company') @staticmethod def default_state(): return 'draft' @staticmethod def groupings(): return [('product',)] @staticmethod def get_cache(grouping): pool = Pool() if all(g == 'product' or g.startswith('product.') for g in grouping): return pool.get('stock.period.cache') def get_rec_name(self, name): return str(self.date) @classmethod @ModelView.button @Workflow.transition('draft') def draft(cls, periods): for grouping in cls.groupings(): Cache = cls.get_cache(grouping) caches = [] for sub_periods in grouped_slice(periods): caches.append(Cache.search([ ('period', 'in', [p.id for p in sub_periods]), ], order=[])) Cache.delete(list(chain(*caches))) @classmethod @ModelView.button @Workflow.transition('closed') def close(cls, periods): pool = Pool() Product = pool.get('product.product') Location = pool.get('stock.location') Move = pool.get('stock.move') Date = pool.get('ir.date') transaction = Transaction() connection = transaction.connection database = transaction.database # XXX: A move in the period could be inserted before the lock # from a different transaction. It will not be taken in the pbl # computation but it is quite rare because only past periods are # closed. database.lock(connection, Move._table) if database.has_select_for(): move = Move.__table__() query = move.select(Literal(1), for_=For('UPDATE', nowait=True)) with connection.cursor() as cursor: cursor.execute(*query) locations = Location.search([ ('type', 'not in', ['warehouse', 'view']), ], order=[]) today = Date.today() recent_date = max(period.date for period in periods) if recent_date >= today: cls.raise_user_error('close_period_future_today') if Move.search([ ('state', '=', 'assigned'), ['OR', [ ('effective_date', '=', None), ('planned_date', '<=', recent_date), ], ('effective_date', '<=', recent_date), ]]): cls.raise_user_error('close_period_assigned_move') for grouping in cls.groupings(): Cache = cls.get_cache(grouping) to_create = [] for period in periods: with Transaction().set_context( stock_date_end=period.date, stock_date_start=None, stock_assign=False, forecast=False, stock_destinations=None, ): pbl = Product.products_by_location( [l.id for l in locations], grouping=grouping) for key, quantity in pbl.items(): values = { 'location': key[0], 'period': period.id, 'internal_quantity': quantity, } for i, field in enumerate(grouping, 1): values[field] = key[i] to_create.append(values) if to_create: Cache.create(to_create)
class InternalApply(ModelView): 'Internal Apply' __name__ = 'hrp_internal_delivery.internal_apply' _rec_name = 'number' number = fields.Char('Number', size=None, select=True) type = fields.Selection( [ ('00', u'常规药请领单'), ('06', u'直送药请领单'), ('2017', u' '), ], 'Type', select=True, required=True, states={ 'readonly': Bool(Eval('moves'))}) starts = fields.Selection([ ('00', u'西药'), ('01', u'中成药'), ('02', u'中草药'), ('03', u'颗粒中'), ('04', u'原料药'), ('05', u'敷药'), ('07', u'同位素'), ], 'Starts', sort=False, select=True, states={ 'invisible': Equal(Eval('type'), '2017') }, ) medicine = fields.Selection([ ('00', u''), ('2', u'临采'), ('02', u'精一'), ('03', u'麻醉'), ], 'medicine', select=True, # states={ # 'invisible':~Equal(Eval('starts'),'00')| Equal(Eval('type'),'06') # }, ) special = fields.Selection([ ('00', u'直送'), ], 'special', select=True , states={ 'readonly': Equal(Eval('type'), '06'), 'invisible': ~Equal(Eval('type'), '06'), }, depends=['type'], ) planned_date = fields.Date('Planned Date', readonly=True) moves = fields.One2Many('hrp_internal_delivery.test_apply', 'parents', 'Moves') from_location = fields.Many2One('stock.location', 'from_location', readonly=True) to_location = fields.Many2One('stock.location', 'to_location', readonly=True) warehouse_to_location = fields.Many2One('stock.location', 'warehouse_to_location', select=True) # change_state = fields.Boolean('change_state',select=True)#更改请领状态 @staticmethod def default_type(): return '2017' @staticmethod def default_from_location(): Config = Pool().get('purchase.configuration') config = Config(1) return config.warehouse.storage_location.id # 默认为中心药房 @staticmethod def default_to_location(): UserId = Pool().get('hrp_internal_delivery.test_straight') return UserId.get_user_id() @staticmethod def default_planned_date(): Date = Pool().get('ir.date') today = str(Date.today()) return today @fields.depends('type') def on_change_type(self): if self.type == '06': self.special = '00' else: pass @fields.depends('starts', 'moves', 'special', 'to_location', 'medicine') def on_change_starts(self): Product = Pool().get('product.product') Date = Pool().get('ir.date') MOVE = Pool().get('hrp_new_product.new_product') list = [] if self.special == None: locals = self.to_location if self.medicine == None: mmm = MOVE.search([ ('interim', 'in', ['1', '00']), ('drug_type', '=', self.starts), ('is_direct_sending', '=', False), ('to_location', '=', locals), ]) else: if self.medicine == '2': mmm = MOVE.search([ ('drug_type', '=', self.starts), ('is_direct_sending', '=', False), ('to_location', '=', locals), ('interim', 'in', ['2', '01']), ]) else: mmm = MOVE.search([ ('drug_type', '=', self.starts), ('is_direct_sending', '=', False), ('to_location', '=', locals), ('interim', '=', self.medicine), ]) for i in mmm: dict = {} dict['code'] = i.code dict['product'] = i.product.id dict['product_name'] = i.product.name dict['drug_specifications'] = i.drug_specifications dict['company'] = i.uom dict['a_charge'] = i.a_charge with Transaction().set_context(stock_date_end=Date.today()): quantities = Product.products_by_location([locals], [i.product.id], with_childs=True) if quantities.values(): stock_level = [v for v in quantities.values()][0] else: stock_level = 0.0 dict['stock_level'] = str(stock_level) outpatient_ = str(i.outpatient_7days) dict['outpatient_7days'] = outpatient_.ljust(5, ' ') stock_levels = float(i.outpatient_7days) - stock_level if stock_levels <= 0: dict['odd_numbers'] = '0.0' else: dict['odd_numbers'] = str(stock_levels) dict['is_direct_sending'] = i.is_direct_sending dict['lot'] = i.lot dict['is_collar'] = False dict['party'] = i.party dict['unit_price'] = i.unit_price list.append(dict) self.moves = list if self.special == '00': locals = self.to_location mmm = MOVE.search([ ('drug_type', '=', self.starts), ('is_direct_sending', '=', True), ('to_location', '=', locals), ]) for i in mmm: dict = {} dict['code'] = i.code dict['product'] = i.product.id dict['product_name'] = i.product.name dict['drug_specifications'] = i.drug_specifications dict['company'] = i.uom dict['odd_numbers'] = i.odd_numbers dict['a_charge'] = i.a_charge with Transaction().set_context(stock_date_end=Date.today()): quantities = Product.products_by_location([locals], [i.product.id], with_childs=True) if quantities.values(): stock_level = [v for v in quantities.values()][0] else: stock_level = 0.0 dict['stock_level'] = str(stock_level) if i.outpatient_7days == 0: dict['outpatient_7days'] = '0.0' else: dict['outpatient_7days'] = str(i.outpatient_7days) stock_levels = i.outpatient_7days - stock_level if stock_levels <= 0: dict['odd_numbers'] = '0.0' else: dict['odd_numbers'] = stock_levels dict['is_direct_sending'] = i.is_direct_sending dict['lot'] = i.lot dict['is_collar'] = i.is_collar dict['party'] = i.party dict['unit_price'] = i.unit_price list.append(dict) self.moves = list # 西药特殊,精神一,麻醉 @fields.depends('medicine', 'moves', 'starts', 'special', 'to_location') def on_change_medicine(self): Product = Pool().get('product.product') Date = Pool().get('ir.date') MOVE = Pool().get('hrp_new_product.new_product') list = [] locals = self.to_location if self.special == None: search_move = MOVE.search([ ('is_direct_sending', '=', False), ('interim', '=', self.medicine), ('drug_type', '=', self.starts), ('to_location', '=', locals), ]) for i in search_move: dict = {} dict['code'] = i.code dict['product'] = i.product.id dict['product_name'] = i.product.name dict['drug_specifications'] = i.drug_specifications dict['company'] = i.uom dict['a_charge'] = i.a_charge with Transaction().set_context(stock_date_end=Date.today()): quantities = Product.products_by_location([locals], [i.product.id], with_childs=True) if quantities.values(): stock_level = [v for v in quantities.values()][0] else: stock_level = 0.0 dict['stock_level'] = str(stock_level) Ljust = str(i.outpatient_7days) dict['outpatient_7days'] = Ljust.ljust(5, ' ') stock_levels = float(i.outpatient_7days) - stock_level if stock_levels <= 0: dict['odd_numbers'] = '0.0' else: dict['odd_numbers'] = str(stock_levels) dict['is_direct_sending'] = i.is_direct_sending dict['lot'] = i.lot dict['is_collar'] = i.is_collar dict['party'] = i.party dict['unit_price'] = i.unit_price list.append(dict) if self.starts == None: pass if self.special == '00': locals = self.to_location search_move = MOVE.search([ ('drug_type', '=', self.starts), ('interim', '=', self.medicine), ('is_direct_sending', '=', True), ('to_location', '=', locals), ]) for i in search_move: dict = {} dict['code'] = i.code dict['product'] = i.product.id dict['product_name'] = i.product.name dict['drug_specifications'] = i.drug_specifications dict['company'] = i.uom dict['odd_numbers'] = i.odd_numbers dict['a_charge'] = i.a_charge with Transaction().set_context(stock_date_end=Date.today()): quantities = Product.products_by_location([locals], [i.product.id], with_childs=True) if quantities.values(): stock_level = [v for v in quantities.values()][0] else: stock_level = 0.0 dict['stock_level'] = str(stock_level) dict['outpatient_7days'] = i.outpatient_7days stock_levels = i.outpatient_7days - stock_level if stock_levels <= 0: dict['odd_numbers'] = '0.0' else: dict['odd_numbers'] = stock_levels dict['is_direct_sending'] = i.is_direct_sending dict['lot'] = i.lot dict['is_collar'] = i.is_collar dict['party'] = i.party dict['unit_price'] = i.unit_price list.append(dict) self.moves = list @staticmethod def default_actives(): return '00' @staticmethod def default_company(): return Transaction().context.get('company')
class Formulario(ModelSQL, ModelView): 'Formulario' _name = 'cefiro.formulario' _description = __doc__ _rec_name = 'fecha' # name = fields.Char('Nombre del paciente') #Paciente Asociado y datos autocompletados. Implementar esto. Tema de las edades (el paciente tiene una edad actual, y va yendo a consultas con distintas edades). paciente = fields.Many2One('cefiro.paciente', 'Paciente') #Tipo de consulta fecha = fields.Date('Fecha de consulta inicial', required=True) tipoConsulta = fields.Selection([('esp', u'Consulta espontánea'), ('tra', u'Traído'), ('ori', u'Consulta por orientación'), ('der', 'Derivado')], 'Tipo de Consulta') derivador = fields.Selection([('med', u'Especialidad Médica'), ('psiq', 'Psiquiatra'), ('edu', u'Institución Educativa'), ('otra', 'Otra')], 'Derivado por') derivEspec = fields.Char('Especifique') #Motivo de consulta motivoPaciente1 = fields.Char(u'Motivo según el paciente (1)') motivoPaciente1Cod = fields.Char(u'Código') motivoPaciente2 = fields.Char(u'Motivo según el paciente (2)') motivoPaciente2Cod = fields.Char(u'Código') motivoPaciente3 = fields.Char(u'Motivo según el paciente (3)') motivoPaciente3Cod = fields.Char(u'Código') motivoAcompa1 = fields.Char(u'Motivo según el Acompañante (1)') motivoAcompa1Cod = fields.Char(u'Código') motivoAcompa2 = fields.Char(u'Motivo según el Acompañante (2)') motivoAcompa2Cod = fields.Char(u'Código') motivoAcompa3 = fields.Char(u'Motivo según el Acompañante (3)') motivoAcompa3Cod = fields.Char(u'Código') motivoPsico1 = fields.Char(u'Motivo según el Psicólogo (1)') motivoPsico1Cod = fields.Char(u'Código') motivoPsico2 = fields.Char(u'Motivo según el Psicólogo (2)') motivoPsico2Cod = fields.Char(u'Código') motivoPsico3 = fields.Char(u'Motivo según el Psicólogo (3)') motivoPsico3Cod = fields.Char(u'Código') motivoComplementaria = fields.Text(u'Descripción Comlpementaria') #Datos personales extra lugarNacimiento = fields.Char('Lugar de Nacimiento') #Vivienda y Trabajo tipoVivienda = fields.Selection([('casa', 'la casa'), ('calle', 'la calle'), ('inst', u'institución protectora'), ('car', 'privado de libertad')], 'Vive en') #convive = fields.One2Many() Hay que hacer uno para familiares posibles. Por ahora va un Selection. convive = fields.Selection([('solo', 'Vive solo'), ('madre', 'Madre'), ('padre', 'Padre'), ('madra', 'Madrastra'), ('padra', 'Padrastro'), ('her', 'Hermano'), ('pare', 'Pareja'), ('hijo', 'Hijo'), ('otros', 'Otros')], u'Con quién convive') situacionCony = fields.Selection( [('sol', u'Soltero/a'), ('casado', u'Casado/a (incluye separado/a sin divorcio)'), ('divor', u'Divorciado/a'), ('viudo', u'Viudo/a'), ('ulibre', u'Unión libre'), ('slibre', u'Separado de unión libre')], 'Estado Conyugal') vivHabitaTot = fields.Integer('Habitaciones totales') vivHabitaDor = fields.Integer('Dormitorios') vivBanos = fields.Integer(u'Cantidad de Baños') vivBanoComp = fields.Boolean(u'Baño compartido') vivBanoTipo = fields.Selection( [('red', 'Red general'), ('fosa', u'Fosa séptica o pozo negro'), ('otro', u'Otro (hueco en suelo, superficie)')], u'Tipo de instalación sanitaria') trabSituacion = fields.Selection([('trab', 'Trabaja'), ('bus', 'Busca por primera vez'), ('no', 'No trabaja'), ('pas', u'Pasantía'), ('pens', 'Pensionista'), ('jub', 'Jubilado')], u'Situación Laboral') trabMulti = fields.Boolean('Multiempleo') trabHoras = fields.Integer('Horas Totales') trabHorasPrin = fields.Integer('Horas Trabajo Principal') trabInicio = fields.Integer('Edad de inicio de trabajo') trabInfantil = fields.Boolean('Trabajo infantil') trabJuvenil = fields.Boolean('Trabajo juvenil') trabLegal = fields.Boolean('Trabajo legalizado') trabInsalubre = fields.Boolean('Trabajo insalubre') trabTipoRel = fields.Selection([('pub', u'Empleado Público'), ('priv', 'Empleado Privado'), ('indep', 'Empleado Independiente'), ('otro', 'Otro')], u'Tipo de Relación') trabObs = fields.Text('Observaciones') trabPadre = fields.Selection([('trab', 'Trabaja'), ('bus', 'Busca por primera vez'), ('no', 'No trabaja'), ('pas', u'Pasantía'), ('pens', 'Pensionista'), ('jub', 'Jubilado')], u'Situación Laboral del Padre') trabMadre = fields.Selection([('trab', 'Trabaja'), ('bus', 'Busca por primera vez'), ('no', 'No trabaja'), ('pas', u'Pasantía'), ('pens', 'Pensionista'), ('jub', 'Jubilado')], u'Situación Laboral de la Madre') trabPareja = fields.Selection([('trab', 'Trabaja'), ('bus', 'Busca por primera vez'), ('no', 'No trabaja'), ('pas', u'Pasantía'), ('pens', 'Pensionista'), ('jub', 'Jubilado')], u'Situación Laboral de la Pareja') #Educación eduFormalNivel = fields.Selection([('no', 'No escolarizado'), ('priInc', 'Primaria Incompleta'), ('pri', 'Primaria Completa'), ('secInc', 'Secundaria Incompleta'), ('sec', 'Secundaria Completa'), ('tercInc', 'Terciaria Incompleta'), ('terc', 'Terciaria Completa'), ('uniInc', 'Universitaria Incompleta'), ('uni', 'Universitaria Completa')], u'Nivel de Educación Formal') eduFormalNivelMax = fields.Integer( u'Máximo año aprobado (en caso de no haber completado lo último que estudió)' ) eduCentrosPrimaria = fields.Char(u'Centros en los que estudió') eduPubliPrimaria = fields.Selection([('publi', u'Pública'), ('priv', 'Privada')], u'Tipo de institución') eduDifPrimaria = fields.Boolean(u'Presentó dificultades de aprendizaje') eduDifTipoPrimaria = fields.Char(u'Tipo de dificultad que presentó') eduRepePrimaria = fields.Integer( u'Cantidad de años repetidos (si no hay dejar vacío)') eduRepeCausaPrimaria = fields.Char(u'Causa de la repetición de años') eduDeserPrimaria = fields.Boolean(u'Deserción o Exclusión') eduCentrosSecundaria = fields.Char(u'Centros en los que estudió') eduPubliSecundaria = fields.Selection([('publi', u'Pública'), ('priv', 'Privada')], u'Tipo de institución') eduDifSecundaria = fields.Boolean(u'Presentó dificultades de aprendizaje') eduDifTipoSecundaria = fields.Char(u'Tipo de dificultad que presentó') eduRepeSecundaria = fields.Integer( u'Cantidad de años repetidos (si no hay dejar vacío)') eduRepeCausaSecundaria = fields.Char(u'Causa de la repetición de años') eduDeserSecundaria = fields.Boolean(u'Deserción o Exclusión') eduCentrosTerciaria = fields.Char(u'Centros en los que estudió') eduPubliTerciaria = fields.Selection([('publi', u'Pública'), ('priv', 'Privada')], u'Tipo de institución') eduCentrosUniv = fields.Char(u'Centros en los que estudió') eduPubliUniv = fields.Selection([('publi', u'Pública'), ('priv', 'Privada')], u'Tipo de institución') eduNoForCurso1 = fields.Char('Curso') eduNoForCentro1 = fields.Char('Centro') eduNoForPubli1 = fields.Selection([('publi', u'Pública'), ('priv', 'Privada')], u'Tipo de institución') eduNoForAsis1 = fields.Selection([('actual', 'Asiste'), ('pasado', u'Asistió')], 'Momento') eduNoForCurso2 = fields.Char('Curso') eduNoForCentro2 = fields.Char('Centro') eduNoForPubli2 = fields.Selection([('publi', u'Pública'), ('priv', 'Privada')], u'Tipo de institución') eduNoForAsis2 = fields.Selection([('actual', 'Asiste'), ('pasado', u'Asistió')], 'Momento') eduNoForCurso3 = fields.Char('Curso') eduNoForCentro3 = fields.Char('Centro') eduNoForPubli3 = fields.Selection([('publi', u'Pública'), ('priv', 'Privada')], u'Tipo de institución') eduNoForAsis3 = fields.Selection([('actual', 'Asiste'), ('pasado', u'Asistió')], 'Momento') eduPadre = fields.Selection([('no', 'No escolarizado'), ('priInc', 'Primaria Incompleta'), ('pri', 'Primaria Completa'), ('secInc', 'Secundaria Incompleta'), ('sec', 'Secundaria Completa'), ('tercInc', 'Terciaria Incompleta'), ('terc', 'Terciaria Completa'), ('uniInc', 'Universitaria Incompleta'), ('uni', 'Universitaria Completa')], u'Nivel de Educación Formal del Padre') eduMadre = fields.Selection([('no', 'No escolarizado'), ('priInc', 'Primaria Incompleta'), ('pri', 'Primaria Completa'), ('secInc', 'Secundaria Incompleta'), ('sec', 'Secundaria Completa'), ('tercInc', 'Terciaria Incompleta'), ('terc', 'Terciaria Completa'), ('uniInc', 'Universitaria Incompleta'), ('uni', 'Universitaria Completa')], u'Nivel de Educación Formal de la Madre') eduPareja = fields.Selection([('no', 'No escolarizado'), ('priInc', 'Primaria Incompleta'), ('pri', 'Primaria Completa'), ('secInc', 'Secundaria Incompleta'), ('sec', 'Secundaria Completa'), ('tercInc', 'Terciaria Incompleta'), ('terc', 'Terciaria Completa'), ('uniInc', 'Universitaria Incompleta'), ('uni', 'Universitaria Completa')], u'Nivel de Educación Formal de la Pareja') #Antecedentes antPedagogica = fields.Selection([('conc', u'Concluida'), ('aba', 'Abandonada'), ('cur', 'En Curso')], u'Intervención Pedagógica') antPedagogicaPocas = fields.Boolean('Menos de 3 consultas') antPedagogicaMeses = fields.Integer(u'Duración en meses') antPedagogicaMedicacion = fields.Boolean(u'Medicación') antPedagogicaMedTipo = fields.Selection([('ansiolitico', u'Ansiolíticos'), ('antidepre', 'Antidepresivos'), ('neurolep', u'Neurolépticos'), ('otro', 'Otros')], u'Tipo de Medicación') antPedagogicaObs = fields.Char(u'Motivo y Obs.') antMedica = fields.Selection([('conc', u'Concluida'), ('aba', 'Abandonada'), ('cur', 'En Curso')], u'Intervención Médica') antMedicaPocas = fields.Boolean('Menos de 3 consultas') antMedicaMeses = fields.Integer(u'Duración en meses') antMedicaMedicacion = fields.Boolean(u'Medicación') antMedicaMedTipo = fields.Selection([('ansiolitico', u'Ansiolíticos'), ('antidepre', 'Antidepresivos'), ('neurolep', u'Neurolépticos'), ('otro', 'Otros')], u'Tipo de Medicación') antMedicaObs = fields.Char(u'Motivo y Obs.') antPsicologica = fields.Selection([('conc', u'Concluida'), ('aba', 'Abandonada'), ('cur', 'En Curso')], u'Intervención Psicológica') antPsicologicaPocas = fields.Boolean('Menos de 3 consultas') antPsicologicaMeses = fields.Integer(u'Duración en meses') antPsicologicaMedicacion = fields.Boolean(u'Medicación') antPsicologicaMedTipo = fields.Selection([('ansiolitico', u'Ansiolíticos'), ('antidepre', 'Antidepresivos'), ('neurolep', u'Neurolépticos'), ('otro', 'Otros')], u'Tipo de Medicación') antPsicologicaObs = fields.Char(u'Motivo y Obs.') antPsiquiatrica = fields.Selection([('conc', u'Concluida'), ('aba', 'Abandonada'), ('cur', 'En Curso')], u'Intervención Psiquiátrica') antPsiquiatricaPocas = fields.Boolean('Menos de 3 consultas') antPsiquiatricaMeses = fields.Integer(u'Duración en meses') antPsiquiatricaMedicacion = fields.Boolean(u'Medicación') antPsiquiatricaMedTipo = fields.Selection( [('ansiolitico', u'Ansiolíticos'), ('antidepre', 'Antidepresivos'), ('neurolep', u'Neurolépticos'), ('otro', 'Otros')], u'Tipo de Medicación') antPsiquiatricaObs = fields.Char(u'Motivo y Obs.') antPsiqInter = fields.Selection([('conc', u'Concluida'), ('aba', 'Abandonada'), ('cur', 'En Curso')], u'Internación Psiquiátrica') antPsiqInterPocas = fields.Boolean('Menos de 3 consultas') antPsiqInterMeses = fields.Integer(u'Duración en meses') antPsiqInterMedicacion = fields.Boolean(u'Medicación') antPsiqInterMedTipo = fields.Selection([('ansiolitico', u'Ansiolíticos'), ('antidepre', 'Antidepresivos'), ('neurolep', u'Neurolépticos'), ('otro', 'Otros')], u'Tipo de Medicación') antPsiqInterObs = fields.Char(u'Motivo y Obs.') antIntOtra = fields.Selection([('conc', u'Concluida'), ('aba', 'Abandonada'), ('cur', 'En Curso')], u'Otra') antIntOtraPocas = fields.Boolean('Menos de 3 consultas') antIntOtraMeses = fields.Integer(u'Duración en meses') antIntOtraMedicacion = fields.Boolean(u'Medicación') antIntOtraMedTipo = fields.Selection([('ansiolitico', u'Ansiolíticos'), ('antidepre', 'Antidepresivos'), ('neurolep', u'Neurolépticos'), ('otro', 'Otros')], u'Tipo de Medicación') antIntOtraObs = fields.Char(u'Motivo y Obs.') antDisca = fields.Boolean(u'Tiene algún tipo de discapacidad') antDiscaTipo = fields.Char('Tipo') antAyudaTec = fields.Boolean(u'Utiliza algún tipo de ayuda técnica') antAyudaTecLentes = fields.Boolean(u'Lentes') antAyudaTecBaston = fields.Boolean(u'Bastón') antAyudaTecAudifono = fields.Boolean(u'Audífono') antAyudaTecOtro = fields.Boolean(u'Otro') antAyudaTecObs = fields.Char('Obs.') antPrestacion = fields.Boolean( u'Beneficiario de prestación por discapacidad') antPrestPension = fields.Boolean(u'Pensión no contributiva') antPrestJubil = fields.Boolean(u'Jubilación por incapacidad') antPrestAsigDoble = fields.Boolean(u'Asignación doble') antPrestAyuda = fields.Boolean(u'Ayuda especial') antPrestEquidad = fields.Boolean(u'Plan de equidad') antCeguera = fields.Boolean(u'Ceguera y/o dism. de visión') antSordera = fields.Boolean(u'Sordera / hipoacusia') antMotriz = fields.Boolean(u'Ceguera y/o dism. de visión') antDependencia = fields.Boolean(u'Dependencia de otra persona') antAsma = fields.Boolean(u'Asma') antEpilepsia = fields.Boolean(u'Epilepsia') antDiabetes = fields.Boolean(u'Diabetes') antTiroides = fields.Boolean(u'Enf. Tiroidea') antCancer = fields.Boolean(u'Cáncer') antVIH = fields.Boolean(u'VIH/SIDA') antOsteo = fields.Boolean(u'Pat. Osteoarticular') antCardio = fields.Boolean(u'Enf. Cardiovascular') antAccidente1Edad = fields.Integer('Edad') antAccidente1Tipo = fields.Char('Tipo') antAccidente2Edad = fields.Integer('Edad') antAccidente2Tipo = fields.Char('Tipo') antCirugia1Edad = fields.Integer('Edad') antCirugia1Tipo = fields.Char('Tipo') antCirugia2Edad = fields.Integer('Edad') antCirugia2Tipo = fields.Char('Tipo') antAutoeliminCant = fields.Integer( u'Cantidad de intentos de autoeliminación') antAutoelim1Edad = fields.Integer('Edad') antAutoelim1Tipo = fields.Char('Tipo') antAutoelim2Edad = fields.Integer('Edad') antAutoelim2Tipo = fields.Char('Tipo') #Violencia y Uso de Sustancias tvioDanoPsico = u'¿Su pareja o alguien importante para usted le ha causado daño emocional o psicológico en forma repetida?\n (Por ej.: por medio de alguna de las siguientes situaciones: insultos, maltrato a sus hijos, hacerlo/a\n sentir avergonzado/a o humillado/a desprecio por las tareas que usted realiza, destrucción de objetos\n de amigos o parientes, otras.)' vioDanoPsico = fields.Selection([('si', u'Sí'), ('no', 'No'), ('nc', 'No desea contestar')], tvioDanoPsico) vioDanoPsicoQuien = fields.Char(u'¿Quién/es lo hizo/cieron?') vioDanoPsicoNino = fields.Boolean(u'Niño/a') vioDanoPsicoAdoles = fields.Boolean(u'Adolescente') vioDanoPsicoJoven = fields.Boolean(u'Joven') vioDanoPsicoAdulto = fields.Boolean(u'Adulto/a') vioDanoPsicoMayor = fields.Boolean(u'Mayor de 65') vioDanoPsicoEmbarazo = fields.Boolean(u'Embarazo/postparto') vioDanoPsicoActual = fields.Selection([('si', u'Sí'), ('no', 'No'), ('nc', 'No desea contestar')], u'¿Sucede actualmente?') tvioDanoFisico = u'¿Su pareja o alguien importante para usted le ha causado daño físico grave al menos una vez, o le ha hecho agresiones menores en forma reiterada?\n (Por ej.: empujones, golpe de puños, quemaduras, zamarreos, mordeduras, ahorcamiento, pellizcos, palizas, golpes con objetos,\n tirón de pelo, patadas, daño con armas, cachetadas, otra forma.)' vioDanoFisico = fields.Selection([('si', u'Sí'), ('no', 'No'), ('nc', 'No desea contestar')], tvioDanoFisico) vioDanoFisicoQuien = fields.Char(u'¿Quién/es lo hizo/cieron?') vioDanoFisicoNino = fields.Boolean(u'Niño/a') vioDanoFisicoAdoles = fields.Boolean(u'Adolescente') vioDanoFisicoJoven = fields.Boolean(u'Joven') vioDanoFisicoAdulto = fields.Boolean(u'Adulto/a') vioDanoFisicoMayor = fields.Boolean(u'Mayor de 65') vioDanoFisicoEmbarazo = fields.Boolean(u'Embarazo/postparto') vioDanoFisicoActual = fields.Selection([('si', u'Sí'), ('no', 'No'), ('nc', 'No desea contestar')], u'¿Sucede actualmente?') tvioDanoSexual = u'¿Cuando usted era niño/a recuerda haber sido tocado/a de manera inapropiada por alguien o haber tenido relaciones o contacto sexual?' vioDanoSexual = fields.Selection([('si', u'Sí'), ('no', 'No'), ('nc', 'No desea contestar')], tvioDanoSexual) vioDanoSexualQuien = fields.Char(u'¿Quién/es lo hizo/cieron?') tvioViola = u'¿Alguna vez en su vida ha sido obligado/a a tener relaciones o contacto sexual?\n (Por ej.: empleo de la fuerza física, de intimidación o amenaza para mantener relaciones sexuales no deseadas.)' vioViola = fields.Selection([('si', u'Sí'), ('no', 'No'), ('nc', 'No desea contestar')], tvioViola) vioViolaQuien = fields.Char(u'¿Quién/es lo hizo/cieron?') vioViolaNino = fields.Boolean(u'Niño/a') vioViolaAdoles = fields.Boolean(u'Adolescente') vioViolaJoven = fields.Boolean(u'Joven') vioViolaAdulto = fields.Boolean(u'Adulto/a') vioViolaMayor = fields.Boolean(u'Mayor de 65') vioViolaEmbarazo = fields.Boolean(u'Embarazo/postparto') vioViolaActual = fields.Selection([('si', u'Sí'), ('no', 'No'), ('nc', 'No desea contestar')], u'¿Sucede actualmente?') vioPensamiento = fields.Selection([ ('si', u'Sí'), ('no', 'No'), ('nc', 'No desea contestar') ], u'Hoy, en su casa, ¿piensa usted que podría sufrir alguna de las situaciones antes nombradas?' ) tsustAlcohol = u'Durante los últimos 30 días, ¿con qué frecuencia usted bebió al menos 4 medidas de cualquier\n clase de bebida con alcohol en un mismo día?' sustAlcohol = fields.Selection( [('nunca', 'Nunca'), ('unames', 'Una vez al mes'), ('dosmes', '2 o 3 veces al mes'), ('unasemana', 'Una vez a la semana'), ('dossemana', u'2 dóas a la semana o más')], tsustAlcohol) tsustCigarro = u'Durante los últimos 30 días, ¿con qué frecuencia usted fumó cigarrillos, tabaco o pipa?' sustCigarro = fields.Selection( [('nunca', 'Nunca'), ('unames', 'Una vez al mes'), ('dosmes', '2 o 3 veces al mes'), ('unasemana', 'Una vez a la semana'), ('dossemana', u'2 dóas a la semana o más')], tsustCigarro) tsustMedicamento = u'Durante los últimos 30 días, ¿con qué frecuencia usted usó algunos de los siguientes medicamentos \n POR SU CUENTA (esto es sin una receta de su médico o en cantidades mayores a las recetadas)?\n Medicamentos para el dolor como tramadol o morfina, estimulantes como ritalina, tranquilizantes como Lexotán.' sustMedicamento = fields.Selection( [('nunca', 'Nunca'), ('unames', 'Una vez al mes'), ('dosmes', '2 o 3 veces al mes'), ('unasemana', 'Una vez a la semana'), ('dossemana', u'2 dóas a la semana o más')], tsustMedicamento) tsustDroga = u'Durante los últimos 30 días, ¿con qué frecuencia usted usó algunas de las siguientes sustancias:\n Marihuana, Cocaína, Pasta Base, Crack, Estimulantes como éxtasis, Halucinógenos como hongos o LSD,\n Heroína, Inhalantes como pegamento?' sustDroga = fields.Selection([('nunca', 'Nunca'), ('unames', 'Una vez al mes'), ('dosmes', '2 o 3 veces al mes'), ('unasemana', 'Una vez a la semana'), ('dossemana', u'2 dóas a la semana o más')], tsustDroga)
class OrthancStudy(ModelSQL, ModelView): """Orthanc study""" __name__ = "gnuhealth.orthanc.study" patient = fields.Many2One("gnuhealth.orthanc.patient", "Patient", readonly=True) uuid = fields.Char("UUID", readonly=True, required=True) description = fields.Char("Description", readonly=True) date = fields.Date("Date", readonly=True) ident = fields.Char("ID", readonly=True) institution = fields.Char( "Institution", readonly=True, help="Imaging center where study was undertaken" ) ref_phys = fields.Char("Referring Physician", readonly=True) req_phys = fields.Char("Requesting Physician", readonly=True) server = fields.Many2One("gnuhealth.orthanc.config", "Server", readonly=True) link = fields.Function( fields.Char("URL", help="Link to study in Orthanc Explorer"), "get_link" ) imaging_test = fields.Many2One("gnuhealth.imaging.test.result", "Test") def get_link(self, name): pre = "".join([self.server.domain.rstrip("/"), "/"]) add = "app/explorer.html#study?uuid={}".format(self.uuid) return urljoin(pre, add) @classmethod def __setup__(cls): super().__setup__() t = cls.__table__() cls._sql_constraints = [ ( "uuid_unique", Unique(t, t.server, t.uuid), "UUID must be unique for a given server", ) ] def get_rec_name(self, name): return ": ".join((self.ident or self.uuid, self.description or "")) @staticmethod def get_info_from_dicom(studies): """Extract information for writing to database""" data = [] for study in studies: try: date = datetime.strptime( study["MainDicomTags"]["StudyDate"], "%Y%m%d" ).date() except: date = None try: description = study["MainDicomTags"]["RequestedProcedureDescription"] except: description = None data.append( { "parent_patient": study["ParentPatient"], "uuid": study["ID"], "description": description, "date": date, "ident": study.get("MainDicomTags").get("StudyID"), "institution": study.get("MainDicomTags").get("InstitutionName"), "ref_phys": study.get("MainDicomTags").get( "ReferringPhysicianName" ), "req_phys": study.get("MainDicomTags").get("RequestingPhysician"), } ) return data @classmethod def update_studies(cls, studies, server): """Update studies""" entries = cls.get_info_from_dicom(studies) updates = [] for entry in entries: try: study = cls.search( [("uuid", "=", entry["uuid"]), ("server", "=", server)], limit=1 )[0] study.description = entry["description"] study.date = entry["date"] study.ident = entry["ident"] study.institution = entry["institution"] study.ref_phys = entry["ref_phys"] study.req_phys = entry["req_phys"] updates.append(study) logger.info("Updating study {}".format(entry["uuid"])) except: continue logger.warning("Unable to update study {}".format(entry["uuid"])) cls.save(updates) @classmethod def create_studies(cls, studies, server): """Create studies""" pool = Pool() Patient = pool.get("gnuhealth.orthanc.patient") entries = cls.get_info_from_dicom(studies) for entry in entries: try: patient = Patient.search( [("uuid", "=", entry["parent_patient"]), ("server", "=", server)], limit=1, )[0] except: patient = None logger.warning( "No parent patient found for study {}".format(entry["ID"]) ) entry.pop("parent_patient") # remove non-model entry entry["server"] = server entry["patient"] = patient cls.create(entries)
class HouseBuildingLoan(Workflow, ModelSQL, ModelView): 'House Building Loan' __name__ = 'hba.loan' salary_code = fields.Char('Salary Code', states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state'], required=True) employee = fields.Many2One('company.employee', 'Name of Applicant', states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state'], required=True) designation = fields.Many2One("employee.designation", "Designation", states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state'], required=True) department = fields.Many2One("company.department", "Department", states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state'], required=True) post_held = fields.Selection([ ('permanent', 'Permanent'), ('temporary_offg', 'Temporary/Offg'), ('length_of_service', 'Length of service on the date of application') ], string='Post held', sort=False, states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state'], required=True) place_of_posting = fields.Char('Place of Posting', states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state'], required=True) scale_pay = fields.Char('Scale Pay', states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state'], required=True) pension_rule = fields.Selection([ ('nps', 'NPS'), ('gpf', 'GPF'), ], string='Pension Rule', sort=False, states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state'], required=True) retirement_date = fields.Date('Date of Retirement', required=True) advance = fields.Selection( [('yes', 'Yes'), ('no', 'No')], string='Any Other advance/Final withdrawal taken\ for purchase of land/construction', sort=False, required=True) advance_needed = fields.Selection( [('plot', 'Purchase of Plot'), ('construction', 'Construction of new House'), ('enlarging_house', 'Enlarging existing House'), ('ready_built', 'Purchase of Ready Built House'), ('own_house', 'Own House')], string='Advance Needed for', sort=False, required=True) advance_amount = fields.Float('Amount', states={ 'invisible': ~Eval('advance').in_(['yes']), }, depends=['advance']) source = fields.Char('Source', states={ 'invisible': ~Eval('advance').in_(['yes']), }, depends=['advance']) attest_copy = fields.Many2One('ir.attachment', 'attested Copy', states={ 'invisible': ~Eval('advance').in_(['yes']), }, depends=['advance']) location_address = fields.Char( 'Location with Address', states={ 'invisible': ~Eval('advance_needed').in_(['plot', 'enlarging_house']), }, depends=['advance_needed']) location_type = fields.Selection([ ('rural', 'Rural'), ('urbun', 'Urban'), ], string='Location Type', sort=False, states={ 'invisible': ~Eval('advance_needed').in_(['plot']), }, depends=['advance_needed']) clearly = fields.Selection([('yes', 'Yes'), ('no', 'No')], string='Is it Clearly Demacrated & Developed', sort=False, states={ 'invisible': ~Eval('advance_needed').in_(['plot']), }, depends=['advance_needed']) area = fields.Float('Apporximate area(in sq.mts.)', states={ 'invisible': ~Eval('advance_needed').in_(['plot']), }, depends=['advance_needed']) cost = fields.Float('cost', states={ 'invisible': ~Eval('advance_needed').in_(['plot']), }, depends=['advance_needed']) amount = fields.Float('Amount actually paid', states={ 'invisible': ~Eval('advance_needed').in_(['plot']), }, depends=['advance_needed']) acquired = fields.Char('If not Purchase when proposed to be acquired', states={ 'invisible': ~Eval('advance_needed').in_(['plot']), }, depends=['advance_needed']) unexpired = fields.Char('Unexpired portion of lease if not free hold', states={ 'invisible': ~Eval('advance_needed').in_(['plot']), }, depends=['advance_needed']) amount_required = fields.Float('Amount of advance required', required=True) installment_no = fields.Integer('No. of instalments for repayment', required=True) construction = fields.One2Many( 'construction', 'hba_loan', 'Construction', states={ 'invisible': ~Eval('advance_needed').in_(['construction']), }, depends=['advance_needed']) floor = fields.Integer('Floor-wise area to be constructed') plinth_area = fields.Float('Plinth area(in sq.mtrs.') enlargement = fields.Float( 'Plinth area proposed for enlargement(in sq. mtrs.)') cunstr_cost = fields.Float('Construction Cost') enlargement_cost = fields.Float('Cost of proposed enlargement') total_plinth = fields.Float('Total Plinth area') total_cost = fields.Float('Total Cost') constructed = fields.Date('When Constructed') price_settled = fields.Float('Price Settled') agency = fields.Char('The Agency from whom to be purchased') already_paid = fields.Float('Already paid') to_be_paid = fields.Float('To be Paid') own_house = fields.One2Many('own.house', 'hba_loan', 'Own House', states={ 'invisible': ~Eval('advance_needed').in_(['own_house']), }, depends=['advance_needed']) loan_line = fields.One2Many('hba.loan.line', 'loan', 'Installment Lines', readonly=True) payout = fields.Float('Payout', states={ 'readonly': ~Eval('state').in_(['draft']), 'invisible': ~Eval('refund').in_(['refundable']), }, depends=['state']) pending = fields.Float('Pending', states={ 'readonly': ~Eval('state').in_(['draft']), 'invisible': ~Eval('refund').in_(['refundable']), }, depends=['state']) reschedule = fields.Float('Reschedule', states={ 'readonly': ~Eval('state').in_(['draft']), 'invisible': ~Eval('refund').in_(['refundable']), }, depends=['state']) state = fields.Selection([('draft', 'Draft'), ('forwarded_to_jo', 'Forwarded to JO'), ('forwarded_to_ao', 'Forwarded to AO'), ('approve', 'Approved'), ('cancel', 'Cancel')], 'Status', readonly=True, sort=False) cancel_reason = fields.Char( 'Cancel Reason', states={ 'invisible': ~Eval('state').in_(['forwarded_to_ao', 'cancel']), 'readonly': ~Eval('state').in_(['forwarded_to_ao']), }, depends=['state'], ) check = fields.Boolean('Check', states={ 'invisible': Eval('state').in_([ 'draft', 'forwarded_to_ao', 'forwarded_to_jo', 'approve', 'cancel' ]), }, depends=['state']) @staticmethod def default_state(): return 'draft' @staticmethod def default_employee(): pool = Pool() User = pool.get('res.user') user = User(Transaction().user) employee = user.employee return employee.id if employee else None @staticmethod def default_salary_code(): pool = Pool() User = pool.get('res.user') user = User(Transaction().user) employee = user.employee return employee.salary_code if employee else None @staticmethod def default_designation(): pool = Pool() User = pool.get('res.user') user = User(Transaction().user) employee = user.employee return employee.designation.id if employee.designation else None @staticmethod def default_department(): pool = Pool() User = pool.get('res.user') user = User(Transaction().user) employee = user.employee return employee.department.id if employee.department else None @staticmethod def default_pay_in_band(): pool = Pool() User = pool.get('res.user') user = User(Transaction().user) employee = user.employee return employee.pay_in_band if employee else None @staticmethod def default_pension_rule(): pool = Pool() User = pool.get('res.user') user = User(Transaction().user) employee = user.employee return employee.gpf_nps if employee else None @classmethod def validate(cls, records): """Method to validate records(rows) in a model(table)""" super().validate(records) for record in records: house_loan = cls.search([('id', '!=', record.id), ('employee', '=', record.employee), ('state', '=', 'approve')]) if house_loan: cls.raise_user_error('You have alraedy take house loan') @classmethod def view_attributes(cls): """Define states for attributes in form view""" plot = ~Eval('advance_needed').in_(['plot']) construction = ~Eval('advance_needed').in_(['construction']) enlarging_house = ~Eval('advance_needed').in_(['enlarging_house']) ready_built = ~Eval('advance_needed').in_(['ready_built']) own_house = ~Eval('advance_needed').in_(['own_house']) attribute = [ ("//page[@id='plot']", "states", { "invisible": plot }), ("//page[@id='construction']", "states", { "invisible": construction }), ("//page[@id='existing_house']", "states", { "invisible": enlarging_house }), ("//page[@id='build_house']", "states", { "invisible": ready_built }), ("//page[@id='own_house']", "states", { "invisible": own_house }), ] return attribute @fields.depends('employee') def on_change_employee(self, name=None): if self.employee: self.salary_code = self.employee.salary_code self.designation = self.employee.designation self.department = self.employee.department self.pay_in_band = self.employee.pay_in_band if self.employee.gpf_nps: self.pension_rule = self.employee.gpf_nps @classmethod def __setup__(cls): """Setup workflow transitions and button properties when an instance of this class is initialized""" super().__setup__() cls._transitions |= set(( ('draft', 'forwarded_to_jo'), ('forwarded_to_jo', 'forwarded_to_ao'), ('forwarded_to_ao', 'approve'), ('forwarded_to_ao', 'cancel'), )) cls._buttons.update({ 'calculate_instalment': {}, 'submitted_to_jo': { 'invisible': ~Eval('state').in_(['draft']), 'depends': ['state'], }, 'forward_to_jo': { 'invisible': ~Eval('state').in_(['forwarded_to_jo']), 'depends': ['state'], }, 'forward_to_ao': { 'invisible': ~Eval('state').in_(['forwarded_to_ao']), 'depends': ['state'], }, 'cancel': { 'invisible': ~Eval('state').in_(['forwarded_to_ao']), 'depends': ['state'], }, }) @classmethod def calculate_instalment(cls, records): for record in records: if record.check == False: cls.loan_installment(records) record.check = True record.save() @classmethod @ModelView.button @Workflow.transition('forwarded_to_jo') def submitted_to_jo(cls, records): """Change status of loan application to forwarded_to_jo""" pass @classmethod @ModelView.button @Workflow.transition('forwarded_to_ao') def forward_to_jo(cls, records): """Change status of loan application to forwarded_to_ao""" pass @classmethod @ModelView.button @Workflow.transition('approve') def forward_to_ao(cls, records): """Change status of loan application to approve""" pass @classmethod @ModelView.button @Workflow.transition('cancel') def cancel(cls, records): """Change status of loan application to cancel""" for record in records: if not record.cancel_reason: cls.raise_user_error('Please fill the Cancel reason') pass @classmethod def loan_installment(cls, records): """Calculate number of installments for loan""" count = 0 LoanLine = Pool().get('hba.loan.line') for loan in records: amount = (loan.amount_required / loan.installment_no) for line in range(1, int(loan.installment_no) + 1): mydate = datetime.now().month month = mydate - 1 if month + line > 12: count += 1 if count > 12: count = 1 months = datetime.now().date(1900, count, 1).strftime('%B') else: months = datetime.now().date(1900, month + line, 1).strftime('%B') vals = { 'month': months, 'amount': amount, 'status': 'pending', 'loan': loan.id } line = LoanLine.create([vals]) @classmethod def write(cls, *args): """ Override default write method of model """ actions = iter(args) for mechanisms, values in zip(actions, actions): if 'installment_no' in values.keys( ) or 'amount_required' in values.keys(): cls.change_loan_installment(mechanisms, values) super().write(*args) @classmethod def change_loan_installment(cls, records, values): """Change number of installments pending for loan""" cursor = Transaction().connection.cursor() LoanLine = Pool().get('hba.loan.line') amount = 0 for loan in records: cursor.execute( 'SELECT sum(amount) FROM hba_loan_line \ WHERE loan=%s AND status != %s', (loan.id, 'pending')) total_amount = cursor.fetchone() if total_amount[0]: reschedule = loan.amount_required - total_amount[0] cls.write(records, { 'payout': total_amount[0], 'reschedule': reschedule }) amount = (reschedule / values['installment_no']) else: if 'installment_no' in values.keys(): amount = (loan.amount_required / values['installment_no']) elif 'amount_required' in values.keys(): amount = (values['amount_required'] / loan.installment_no) elif 'installment_no' in values.keys( ) and 'amount_required' in values.keys(): amount = (values['amount_required'] / values['installment_no']) cursor.execute( 'delete FROM hba_loan_line WHERE loan=%s \ AND status = %s', (loan.id, 'pending')) count = 0 installment_no = 0 if 'installment_no' in values.keys(): installment_no = values['installment_no'] else: installment_no = loan.installment_no for line in range(1, int(installment_no) + 1): mydate = datetime.now().month if total_amount[0]: month = mydate else: month = mydate - 1 if month + line > 12: count += 1 if count > 12: count = 1 months = datetime(1900, count, 1).date().strftime('%B') else: months = datetime(1900, month + line, 1).date().strftime('%B') vals = { 'month': months, 'amount': amount, 'status': 'pending', 'loan': loan.id } line = LoanLine.create([vals])
class OrthancPatient(ModelSQL, ModelView): """Orthanc patient information""" __name__ = "gnuhealth.orthanc.patient" patient = fields.Many2One( "gnuhealth.patient", "Patient", help="Local linked patient" ) name = fields.Char("PatientName", readonly=True) bd = fields.Date("Birthdate", readonly=True) ident = fields.Char("PatientID", readonly=True) uuid = fields.Char("PatientUUID", readonly=True, required=True) studies = fields.One2Many( "gnuhealth.orthanc.study", "patient", "Studies", readonly=True ) server = fields.Many2One("gnuhealth.orthanc.config", "Server", readonly=True) link = fields.Function( fields.Char("URL", help="Link to patient in Orthanc Explorer"), "get_link" ) def get_link(self, name): pre = "".join([self.server.domain.rstrip("/"), "/"]) add = "app/explorer.html#patient?uuid={}".format(self.uuid) return urljoin(pre, add) @classmethod def __setup__(cls): super().__setup__() t = cls.__table__() cls._sql_constraints = [ ( "uuid_unique", Unique(t, t.server, t.uuid), "UUID must be unique for a given server", ) ] @staticmethod def get_info_from_dicom(patients): """Extract information for writing to database""" data = [] for patient in patients: try: bd = datetime.strptime( patient["MainDicomTags"]["PatientBirthDate"], "%Y%m%d" ).date() except: bd = None data.append( { "name": patient.get("MainDicomTags").get("PatientName"), "bd": bd, "ident": patient.get("MainDicomTags").get("PatientID"), "uuid": patient.get("ID"), } ) return data @classmethod def update_patients(cls, patients, server): """Update patients""" Patient = pool.get("gnuhealth.patient") entries = cls.get_info_from_dicom(patients) updates = [] for entry in entries: try: patient = cls.search( [("uuid", "=", entry["uuid"]), ("server", "=", server)], limit=1 )[0] patient.name = entry["name"] patient.bd = entry["bd"] patient.ident = entry["ident"] if not patient.patient: # don't update unless no patient attached try: g_patient = Patient.search( [("puid", "=", entry["ident"])], limit=1 )[0] patient.patient = g_patient logger.info( "New Matching PUID found for {}".format(entry["ident"]) ) except: pass updates.append(patient) logger.info("Updating patient {}".format(entry["uuid"])) except: continue logger.warning("Unable to update patient {}".format(entry["uuid"])) cls.save(updates) @classmethod def create_patients(cls, patients, server): """Create patients""" pool = Pool() Patient = pool.get("gnuhealth.patient") entries = cls.get_info_from_dicom(patients) for entry in entries: try: g_patient = Patient.search([("puid", "=", entry["ident"])], limit=1)[0] logger.info("Matching PUID found for {}".format(entry["uuid"])) except: g_patient = None entry["server"] = server entry["patient"] = g_patient cls.create(entries)
class PurchaseRequest(ModelSQL, ModelView): 'Purchase Request' __name__ = 'purchase.request' product = fields.Many2One('product.product', 'Product', required=True, select=True, readonly=True, domain=[('purchasable', '=', True)]) party = fields.Many2One('party.party', 'Party', select=True, states=STATES, depends=DEPENDS) quantity = fields.Float('Quantity', required=True, states=STATES, digits=(16, Eval('uom_digits', 2)), depends=DEPENDS + ['uom_digits']) uom = fields.Many2One('product.uom', 'UOM', required=True, select=True, states=STATES, depends=DEPENDS) uom_digits = fields.Function(fields.Integer('UOM Digits'), 'on_change_with_uom_digits') computed_quantity = fields.Float('Computed Quantity', readonly=True) computed_uom = fields.Many2One('product.uom', 'Computed UOM', readonly=True) purchase_date = fields.Date('Best Purchase Date', readonly=True) supply_date = fields.Date('Expected Supply Date', readonly=True) default_uom_digits = fields.Function(fields.Integer('Default UOM Digits'), 'on_change_with_default_uom_digits') stock_level = fields.Float('Stock at Supply Date', readonly=True, digits=(16, Eval('default_uom_digits', 2)), depends=['default_uom_digits']) warehouse = fields.Many2One('stock.location', "Warehouse", states={ 'required': Eval('warehouse_required', False), }, domain=[('type', '=', 'warehouse')], depends=['warehouse_required'], readonly=True) warehouse_required = fields.Function(fields.Boolean('Warehouse Required'), 'get_warehouse_required') purchase_line = fields.Many2One('purchase.line', 'Purchase Line', readonly=True) purchase = fields.Function(fields.Many2One('purchase.purchase', 'Purchase'), 'get_purchase', searcher='search_purchase') company = fields.Many2One('company.company', 'Company', required=True, readonly=True, domain=[ ('id', If(In('company', Eval('context', {})), '=', '!='), Eval('context', {}).get('company', -1)), ]) origin = fields.Reference('Origin', selection='get_origin', readonly=True, required=True) exception_ignored = fields.Boolean('Ignored Exception') state = fields.Function(fields.Selection([ ('purchased', 'Purchased'), ('done', 'Done'), ('draft', 'Draft'), ('cancel', 'Cancel'), ('exception', 'Exception'), ], 'State'), 'get_state', searcher='search_state') @classmethod def __setup__(cls): super(PurchaseRequest, cls).__setup__() cls._order[0] = ('id', 'DESC') cls._error_messages.update({ 'create_request': ('Purchase requests are only created ' 'by the system.'), 'delete_purchase_line': ('You can not delete purchased ' 'request.'), }) cls._buttons.update({ 'handle_purchase_cancellation_exception': { 'invisible': Eval('state') != 'exception', }, }) @classmethod def __register__(cls, module_name): pool = Pool() ModelData = pool.get('ir.model.data') TableHandler = backend.get('TableHandler') model_data = ModelData.__table__() super(PurchaseRequest, cls).__register__(module_name) # Migration from 3.6: removing the constraint on the quantity tablehandler = TableHandler(cls, module_name) tablehandler.drop_constraint('check_purchase_request_quantity') # Migration from 3.8: renaming module of Purchase Request group entry cursor = Transaction().connection.cursor() cursor.execute(*model_data.update( columns=[model_data.module], values=['purchase_request'], where=((model_data.fs_id == 'group_purchase_request') & (model_data.module == 'stock_supply')))) def get_rec_name(self, name): if self.warehouse: return "%s@%s" % (self.product.name, self.warehouse.name) else: return self.product.name @classmethod def search_rec_name(cls, name, clause): res = [] names = clause[2].split('@', 1) res.append(('product.template.name', clause[1], names[0])) if len(names) != 1 and names[1]: res.append(('warehouse', clause[1], names[1])) return res @staticmethod def default_company(): return Transaction().context.get('company') @staticmethod def default_exception_ignored(): return False def get_purchase(self, name): if self.purchase_line: return self.purchase_line.purchase.id @classmethod def search_purchase(cls, name, clause): return [('purchase_line.purchase', ) + tuple(clause[1:])] @property def currency(self): return self.company.currency def get_state(self, name): if self.purchase_line: if (self.purchase_line.purchase.state == 'cancel' and not self.exception_ignored): return 'exception' elif self.purchase_line.purchase.state == 'cancel': return 'cancel' elif self.purchase_line.purchase.state == 'done': return 'done' else: return 'purchased' return 'draft' @classmethod def search_state(cls, name, clause): pool = Pool() Purchase = pool.get('purchase.purchase') PurchaseLine = pool.get('purchase.line') request = cls.__table__() purchase_line = PurchaseLine.__table__() purchase = Purchase.__table__() _, operator_, state = clause Operator = fields.SQL_OPERATORS[operator_] state_case = Case( ((purchase.state == 'cancel') & (request.exception_ignored == False), 'exception'), ((purchase.state == 'cancel') & (request.exception_ignored == True), 'cancel'), (purchase.state == 'done', 'done'), (request.purchase_line != Null, 'purchased'), else_='draft') state_query = request.join( purchase_line, type_='LEFT', condition=request.purchase_line == purchase_line.id).join( purchase, type_='LEFT', condition=purchase_line.purchase == purchase.id).select( request.id, where=Operator(state_case, state)) return [('id', 'in', state_query)] def get_warehouse_required(self, name): return self.product.type in ('goods', 'assets') @fields.depends('uom') def on_change_with_uom_digits(self, name=None): if self.uom: return self.uom.digits return 2 @fields.depends('product') def on_change_with_default_uom_digits(self, name=None): if self.product: return self.product.default_uom.digits return 2 @classmethod def _get_origin(cls): 'Return the set of Model names for origin Reference' return set() @classmethod def get_origin(cls): pool = Pool() IrModel = pool.get('ir.model') models = IrModel.search([ ('model', 'in', list(cls._get_origin())), ]) return [(m.model, m.name) for m in models] @classmethod def create(cls, vlist): for vals in vlist: for field_name in ('product', 'quantity', 'uom', 'company'): if vals.get(field_name) is None: cls.raise_user_error('create_request') return super(PurchaseRequest, cls).create(vlist) @classmethod def delete(cls, requests): if any(r.purchase_line for r in requests): cls.raise_user_error('delete_purchase_line') super(PurchaseRequest, cls).delete(requests) @classmethod def find_best_supplier(cls, product, date): ''' Return the best supplier and purchase_date for the product. ''' Date = Pool().get('ir.date') supplier = None today = Date.today() for product_supplier in product.product_suppliers: supply_date = product_supplier.compute_supply_date(date=today) timedelta = date - supply_date if not supplier and timedelta >= datetime.timedelta(0): supplier = product_supplier.party break if supplier: purchase_date = product_supplier.compute_purchase_date(date) else: purchase_date = today return supplier, purchase_date @classmethod @ModelView.button_action( 'purchase_request.wizard_purchase_cancellation_handle_exception') def handle_purchase_cancellation_exception(cls, purchases): pass
class HealthService(ModelSQL, ModelView): 'Health Service' __name__ = 'gnuhealth.health_service' STATES = {'readonly': Eval('state') == 'invoiced'} name = fields.Char('ID', readonly=True) desc = fields.Char('Description') patient = fields.Many2One('gnuhealth.patient', 'Patient', required=True, states=STATES) institution = fields.Many2One('gnuhealth.institution', 'Institution') service_date = fields.Date('Date') service_line = fields.One2Many('gnuhealth.health_service.line', 'name', 'Service Line', help="Service Line") state = fields.Selection([ ('draft', 'Draft'), ('invoiced', 'Invoiced'), ], 'State', readonly=True) invoice_to = fields.Many2One('party.party', 'Invoice to') @classmethod def __setup__(cls): super(HealthService, cls).__setup__() t = cls.__table__() cls._sql_constraints = [ ('name_unique', Unique(t, t.name), 'The Service ID must be unique'), ] cls._buttons.update({ 'button_set_to_draft': { 'invisible': Equal(Eval('state'), 'draft') } }) @staticmethod def default_state(): return 'draft' @staticmethod def default_service_date(): return datetime.date.today() @staticmethod def default_institution(): HealthInst = Pool().get('gnuhealth.institution') institution = HealthInst.get_institution() return institution @classmethod @ModelView.button def button_set_to_draft(cls, services): cls.write(services, {'state': 'draft'}) @classmethod def create(cls, vlist): Sequence = Pool().get('ir.sequence') Config = Pool().get('gnuhealth.sequences') vlist = [x.copy() for x in vlist] for values in vlist: if not values.get('name'): config = Config(1) values['name'] = Sequence.get_id( config.health_service_sequence.id) return super(HealthService, cls).create(vlist)
class Party: __name__ = 'party.party' vat_number_city = fields.Char('VAT Number City', states={'readonly': ~Eval('active', True)}) type_document = fields.Selection([ ('', ''), ('04', 'RUC'), ('05', 'Cedula'), ('06', 'Pasaporte'), ('07', 'Consumidor Final'), ], 'Type Document', states={ 'readonly': ~Eval('active', True), }, depends=['active']) mandatory_accounting = fields.Selection([ ('yes', 'Yes'), ('no', 'No'), ], 'Mandatory Accounting', required=False) first_name = fields.Char('Primer Nombre') second_name = fields.Char('Segundo Nombre') first_family_name = fields.Char('Primer Apellido') second_family_name = fields.Char('Segundo Apellido') commercial_name = fields.Char('Commercial Name') type_party = fields.Selection( [ ('', ''), ('sociedad', 'Sociedad'), ('persona_natural', 'Personal natural'), ('contribuyente_especial', 'Contribuyente especial'), ('entidad_publica', 'Entidad del sector publico'), ('companias_seguros', 'Companias de aseguros y reaseguros'), ], 'Type Party', states={ 'readonly': ~Eval('active', True), 'invisible': Eval('type_document') != '04', }) registro_mercantil = fields.Char( 'Registro Mercantil', states={'readonly': ~Eval('active', True)}) start_activities = fields.Date('Start Activities') @classmethod def __setup__(cls): super(Party, cls).__setup__() cls._error_messages.update( {'invalid_vat_number': ('Invalid VAT Number "%s".')}) cls._sql_constraints += [ ('vat_number', 'UNIQUE(vat_number)', 'VAT Number already exists!'), ] @staticmethod def default_type_document(): return '04' @staticmethod def default_mandatory_accounting(): return 'no' @classmethod def search_rec_name(cls, name, clause): parties = cls.search([ ('vat_number', ) + tuple(clause[1:]), ], limit=1) if parties: return [('vat_number', ) + tuple(clause[1:])] return [('name', ) + tuple(clause[1:])] @classmethod def validate(cls, parties): for party in parties: if party.type_document == '04' and bool(party.vat_number): super(Party, cls).validate(parties) def pre_validate(self): if not self.vat_number: return if self.vat_number == '9999999999999': return vat_number = self.vat_number.replace(".", "") if vat_number.isdigit() and len(vat_number) > 9: is_valid = self.compute_check_digit(vat_number) if is_valid: return self.raise_user_error('invalid_vat_number', (self.vat_number, )) def compute_check_digit(self, raw_number): "Compute the check digit - Modulus 10 and 11" factor = 2 x = 0 set_check_digit = None if self.type_document == '04': # Si es RUC valide segun el tipo de tercero if self.type_party == 'persona_natural': if len(raw_number) != 13 or int( raw_number[2]) > 5 or raw_number[-3:] != '001': return number = raw_number[:9] set_check_digit = raw_number[9] for n in number: y = int(n) * factor if y >= 10: y = int(str(y)[0]) + int(str(y)[1]) x += y if factor == 2: factor = 1 else: factor = 2 res = (x % 10) if res == 0: value = 0 else: value = 10 - (x % 10) elif self.type_party == 'entidad_publica': if not len(raw_number) == 13 or raw_number[2] != '6' \ or raw_number[-3:] != '001': return number = raw_number[:8] set_check_digit = raw_number[8] for n in reversed(number): x += int(n) * factor factor += 1 if factor == 8: factor = 2 value = 11 - (x % 11) if value == 11: value = 0 else: if len(raw_number) != 13 or \ (self.type_party in ['sociedad', 'companias_seguros'] \ and int(raw_number[2]) != 9) or raw_number[-3:] != '001': return number = raw_number[:9] set_check_digit = raw_number[9] for n in reversed(number): x += int(n) * factor factor += 1 if factor == 8: factor = 2 value = 11 - (x % 11) if value == 11: value = 0 else: #Si no tiene RUC valide: cedula, pasaporte, consumidor final (cedula) if len(raw_number) != 10: return number = raw_number[:9] set_check_digit = raw_number[9] for n in number: y = int(n) * factor if y >= 10: y = int(str(y)[0]) + int(str(y)[1]) x += y if factor == 2: factor = 1 else: factor = 2 res = (x % 10) if res == 0: value = 0 else: value = 10 - (x % 10) return (set_check_digit == str(value))
class Work(DeactivableMixin, ModelSQL, ModelView): 'Work' __name__ = 'timesheet.work' name = fields.Char('Name', states={ 'invisible': Bool(Eval('origin')), 'required': ~Eval('origin'), }, depends=['origin'], help="The main identifier of the work.") origin = fields.Reference( 'Origin', selection='get_origin', states={ 'invisible': Bool(Eval('name')), 'required': ~Eval('name'), }, depends=['name'], help="Use to relate the time spent to other records.") duration = fields.Function( fields.TimeDelta('Timesheet Duration', 'company_work_time', help="Total time spent on this work."), 'get_duration') timesheet_start_date = fields.Date( 'Timesheet Start', domain=[ If( Eval('timesheet_start_date') & Eval('timesheet_end_date'), ('timesheet_start_date', '<=', Eval('timesheet_end_date')), ()), ], depends=['timesheet_end_date'], help="Restrict adding lines before the date.") timesheet_end_date = fields.Date( 'Timesheet End', domain=[ If( Eval('timesheet_start_date') & Eval('timesheet_end_date'), ('timesheet_end_date', '>=', Eval('timesheet_start_date')), ()), ], depends=['timesheet_start_date'], help="Restrict adding lines after the date.") company = fields.Many2One('company.company', 'Company', required=True, select=True, help="Make the work belong to the company.") timesheet_lines = fields.One2Many('timesheet.line', 'work', 'Timesheet Lines', depends=['active'], states={ 'readonly': Not(Bool(Eval('active'))), }, help="Spend time on this work.") # Self referring field to use for aggregation in graph view work = fields.Function(fields.Many2One('timesheet.work', 'Work'), 'get_work') @classmethod def __setup__(cls): super(Work, cls).__setup__() t = cls.__table__() cls._sql_constraints += [ ('origin_unique', Unique(t, t.origin, t.company), 'timesheet.msg_work_origin_unique_company'), ] @classmethod def __register__(cls, module_name): table_h = cls.__table_handler__(module_name) table = cls.__table__() cursor = Transaction().connection.cursor() super(Work, cls).__register__(module_name) # Migration from 4.0: remove required on name table_h.not_null_action('name', 'remove') # Migration from 4.0: remove parent, left and right if table_h.column_exist('parent'): id2name = {} id2parent = {} cursor.execute(*table.select(table.id, table.parent, table.name)) for id_, parent, name in cursor: id2name[id_] = name id2parent[id_] = parent for id_, name in id2name.items(): parent = id2parent[id_] while parent: name = '%s\\%s' % (id2name[parent], name) parent = id2parent[parent] cursor.execute( *table.update([table.name], [name], where=table.id == id_)) table_h.drop_column('parent') table_h.drop_column('left') table_h.drop_column('right') # Migration from 4.0: remove timesheet_available if table_h.column_exist('timesheet_available'): cursor.execute(*table.delete( where=table.timesheet_available == Literal(False))) table_h.drop_column('timesheet_available') @staticmethod def default_company(): return Transaction().context.get('company') @classmethod def _get_origin(cls): 'Return list of Model names for origin Reference' return [] @classmethod def get_origin(cls): Model = Pool().get('ir.model') models = cls._get_origin() models = Model.search([ ('model', 'in', models), ]) return [('', '')] + [(m.model, m.name) for m in models] @classmethod def get_duration(cls, works, name): pool = Pool() Line = pool.get('timesheet.line') transaction = Transaction() cursor = transaction.connection.cursor() context = transaction.context table_w = cls.__table__() line = Line.__table__() ids = [w.id for w in works] durations = dict.fromkeys(ids, None) where = Literal(True) if context.get('from_date'): where &= line.date >= context['from_date'] if context.get('to_date'): where &= line.date <= context['to_date'] if context.get('employees'): where &= line.employee.in_(context['employees']) query_table = table_w.join(line, 'LEFT', condition=line.work == table_w.id) for sub_ids in grouped_slice(ids): red_sql = reduce_ids(table_w.id, sub_ids) cursor.execute(*query_table.select(table_w.id, Sum(line.duration), where=red_sql & where, group_by=table_w.id)) for work_id, duration in cursor.fetchall(): # SQLite uses float for SUM if duration and not isinstance(duration, datetime.timedelta): duration = datetime.timedelta(seconds=duration) durations[work_id] = duration return durations def get_work(self, name): return self.id def get_rec_name(self, name): if isinstance(self.origin, ModelStorage): return self.origin.rec_name else: return self.name @classmethod def search_rec_name(cls, name, clause): if clause[1].startswith('!') or clause[1].startswith('not '): bool_op = 'AND' else: bool_op = 'OR' return [ bool_op, ('name', ) + tuple(clause[1:]), ] + [('origin.rec_name', ) + tuple(clause[1:]) + (origin, ) for origin in cls._get_origin()] @classmethod def copy(cls, works, default=None): if default is None: default = {} else: default = default.copy() default.setdefault('timesheet_lines', None) return super(Work, cls).copy(works, default=default) @classmethod def validate(cls, works): super(Work, cls).validate(works) for work in works: if work.origin and not work._validate_company(): raise CompanyValidationError( gettext('timesheet.msg_work_company_different_origin', work=work.rec_name)) def _validate_company(self): return True @classmethod def search_global(cls, text): for record, rec_name, icon in super(Work, cls).search_global(text): icon = icon or 'tryton-clock' yield record, rec_name, icon @property def hours(self): if not self.duration: return 0 return self.duration.total_seconds() / 60 / 60
class PlanificationProfessionalLine(ModelSQL, ModelView): 'Planification Professional Line' __name__ = 'lims.planification.professional.line' template = fields.Many2One('lims.template.analysis_sheet', 'Template') laboratory = fields.Many2One('lims.laboratory', 'Laboratory') professional = fields.Many2One('lims.laboratory.professional', 'Professional') date = fields.Date('Date') state = fields.Selection([ ('draft', 'Draft'), ('active', 'Active'), ('validated', 'Validated'), ('done', 'Done'), ], 'State') samples_qty = fields.Function(fields.Integer('# Samples'), 'get_fields') completion_percentage = fields.Function(fields.Numeric('Complete', digits=(1, 4), domain=[ ('completion_percentage', '>=', 0), ('completion_percentage', '<=', 1), ]), 'get_fields') color = fields.Function(fields.Char('Color'), 'get_color') @classmethod def __setup__(cls): super().__setup__() cls._order.insert(0, ('date', 'ASC')) @classmethod def table_query(cls): pool = Pool() Sheet = pool.get('lims.analysis_sheet') Compilation = pool.get('lims.interface.compilation') sheet = Sheet.__table__() compilation = Compilation.__table__() context = Transaction().context where = Literal(True) if context.get('laboratory'): where &= sheet.laboratory == context.get('laboratory') if context.get('from_date'): where &= compilation.date_time >= datetime.combine( context.get('from_date'), time(0, 0)) if context.get('to_date'): where &= compilation.date_time <= datetime.combine( context.get('to_date'), time(23, 59)) columns = [] for fname, field in cls._fields.items(): if hasattr(field, 'set'): continue if fname == 'date': column = Cast(compilation.date_time, 'date').as_(fname) else: column = Column(sheet, fname).as_(fname) columns.append(column) return sheet.join(compilation, condition=sheet.compilation == compilation.id).select(*columns, where=where) @classmethod def get_fields(cls, records, name): pool = Pool() Sheet = pool.get('lims.analysis_sheet') sheets = Sheet.browse(records) return {s.id: getattr(s, name) for s in sheets} def get_color(self, name): return 'lightblue'
class Article(Workflow, ModelSQL, ModelView, CMSMenuItemMixin): "CMS Articles" __name__ = 'nereid.cms.article' _rec_name = 'uri' uri = fields.Char('URI', required=True, select=True, translate=True) title = fields.Char('Title', required=True, select=True, translate=True) content = fields.Text('Content', required=True, translate=True) template = fields.Char('Template', required=True) image = fields.Many2One('nereid.static.file', 'Image') employee = fields.Many2One('company.employee', 'Employee') author = fields.Many2One('nereid.user', 'Author') published_on = fields.Date('Published On') publish_date = fields.Function(fields.Char('Publish Date'), 'get_publish_date') sequence = fields.Integer('Sequence', required=True, select=True) reference = fields.Reference('Reference', selection='allowed_models') description = fields.Text('Short Description') attributes = fields.One2Many('nereid.cms.article.attribute', 'article', 'Attributes') categories = fields.Many2Many( 'nereid.cms.category-article', 'article', 'category', 'Categories', ) content_type = fields.Selection('content_type_selection', 'Content Type', required=True) # Article can have a banner banner = fields.Many2One('nereid.cms.banner', 'Banner') state = fields.Selection([('draft', 'Draft'), ('published', 'Published'), ('archived', 'Archived')], 'State', required=True, select=True, readonly=True) @classmethod def __setup__(cls): super(Article, cls).__setup__() cls._order.insert(0, ('sequence', 'ASC')) cls._transitions |= set(( ('draft', 'published'), ('published', 'draft'), ('published', 'archived'), ('archived', 'draft'), )) cls._buttons.update({ 'archive': { 'invisible': Eval('state') != 'published', }, 'publish': { 'invisible': Eval('state').in_(['published', 'archived']), }, 'draft': { 'invisible': Eval('state') == 'draft', } }) @classmethod def content_type_selection(cls): """ Returns a selection for content_type. """ default_types = [('html', 'HTML'), ('plain', 'Plain Text')] if markdown: default_types.append(('markdown', 'Markdown')) if publish_parts: default_types.append(('rst', 'reStructured TeXT')) return default_types @classmethod def default_content_type(cls): """ Default content_type. """ return 'plain' def __html__(self): """ Uses content_type field to generate html content. Concept from Jinja2's Markup class. """ if self.content_type == 'rst': if publish_parts: res = publish_parts(self.content, writer_name='html') return res['html_body'] self.raise_user_error( "`docutils` not installed, to render rst articles.") if self.content_type == 'markdown': if markdown: return markdown(self.content) self.raise_user_error( "`markdown` not installed, to render markdown article.") return self.content @classmethod @ModelView.button @Workflow.transition('archived') def archive(cls, articles): pass @classmethod @ModelView.button @Workflow.transition('published') def publish(cls, articles): pass @classmethod @ModelView.button @Workflow.transition('draft') def draft(cls, articles): pass @classmethod def allowed_models(cls): MenuItem = Pool().get('nereid.cms.menuitem') return MenuItem.allowed_models() @fields.depends('title', 'uri') def on_change_title(self): if self.title and not self.uri: self.uri = slugify(self.title) @staticmethod def default_template(): return 'article.jinja' @staticmethod def default_employee(): User = Pool().get('res.user') if 'employee' in Transaction().context: return Transaction().context['employee'] user = User(Transaction().user) if user.employee: return user.employee.id if has_request_context() and current_user.employee: return current_user.employee.id @staticmethod def default_author(): if has_request_context(): return current_user.id @staticmethod def default_published_on(): Date = Pool().get('ir.date') return Date.today() @classmethod @route('/article/<uri>') def render(cls, uri): """ Renders the template """ try: article, = cls.search([ ('uri', '=', uri), ('state', '=', 'published'), ]) except ValueError: abort(404) return render_template(article.template, article=article) @classmethod @route('/sitemaps/article-index.xml') def sitemap_index(cls): index = SitemapIndex(cls, []) return index.render() @classmethod @route('/sitemaps/article-<int:page>.xml') def sitemap(cls, page): sitemap_section = SitemapSection(cls, [], page) sitemap_section.changefreq = 'daily' return sitemap_section.render() @classmethod def get_publish_date(cls, records, name): """ Return publish date to render on view """ res = {} for record in records: res[record.id] = str(record.published_on) return res def get_absolute_url(self, **kwargs): return url_for('nereid.cms.article.render', uri=self.uri, **kwargs) @staticmethod def default_state(): if 'published' in Transaction().context: return 'published' return 'draft' def get_menu_item(self, max_depth): """ Return huge dictionary with serialized article category for menu item { title: <display name>, link: <url>, record: <instance of record> # if type_ is `record` } """ return { 'record': self, 'title': self.title, 'link': self.get_absolute_url(), } def atom_id(self): """ Returns an atom ID for the article """ return ('tag:' + current_website.name + ',' + self.publish_date + ':Article/' + str(self.id)) def atom_publish_date(self): """ Returns the article's publish date with timezone set as UTC """ return pytz.utc.localize( datetime.combine(self.published_on, datetime.min.time())) def serialize(self, purpose=None): """ Serialize Article records """ if purpose == 'atom': # The keys in the dictionary returned are used by Werkzeug's # AtomFeed class. return { 'id': self.atom_id(), 'title': self.title, 'author': (self.author.serialize( purpose=purpose) if self.author else None), 'content': self.content, 'content_type': ('text' if self.content_type == 'plain' else 'html'), 'link': { 'rel': 'alternate', 'type': 'text/html', 'href': self.get_absolute_url(external=True), }, 'category': [ category.serialize(purpose=purpose) for category in self.categories ], 'published': self.atom_publish_date(), 'updated': self.write_date or self.atom_publish_date(), } elif hasattr(super(Article, self), 'serialize'): return super(Article, self).serialize(purpose=purpose) @classmethod @route('/article/all.atom') def atom_feed(cls): """ Renders the atom feed for all articles. """ feed = AtomFeed("All Articles", feed_url=request.url, url=request.host_url) for article in cls.search([('state', '=', 'published')]): feed.add(**article.serialize(purpose='atom')) return feed.get_response()
class Hshn(ModelSQL, ModelView): """ Class for business logic of the module hshn. The used fields, set deafult values, button actions and special logic for sending a mail are defined in the Class """ __name__ = "hshn.hshn" company = fields.Many2One('company.company', 'Company', required=True, states={ 'readonly': (Eval('state') != 'draft') | Eval('lines', [0]), }, domain=[ ('id', If(Eval('context', {}).contains('company'), '=', '!='), Eval('context', {}).get('company', -1)), ], depends=['state'], select=True) topic = fields.Char('Topic', required=True, help='Please enter your topic') project_study_spo3 = fields.Selection([ ('None', ''), ('SEPS', 'SEPS'), ('PSBWL', 'PSBWL'), ('PSWIN', 'PSWIN'), ('PSIT', 'PSIT'), ], 'Project study SPO3', states={ 'readonly': Eval('spo_selection') != 'SPO3', 'required': Bool(Eval('spo_selection') != 'SPO4') }, help='Please select the project study for which you will submit the proposal'+ '\n SEPS = Project Study Software development EDV-No: 281531'+ '\n PSBWL = Project Study BWL EDV-No:281542'+ '\n PSWIN = Project Study economic computer science EDV-No: 281560'+ '\n PSIT = Project Study IT-Systems EDV-No:281584') project_study_spo4 = fields.Selection([ ('None', ''), ('PSEIS', 'PSEIS'), ('PSEMU', 'PSEMU'), ('PSIT', 'PSIT'), ('PSAIS', 'PSAIS'), ('PSIM', 'PSIM'), ('PSSMU', 'PSSMU'), ('PSSMM', 'PSSMM'), ('PSSRM', 'PSSRM'), ], 'Project study SPO4', states={ 'readonly': Eval('spo_selection') != 'SPO4', 'required': Bool(Eval('spo_selection') != 'SPO3') }, help='Please select the project study for which you will submit the proposal' + '\n PSEIS = Project Study Enterprise Information Systems EDV-No: 281761' + '\n PSEMU = Project Study Development mobile Enterprise Applications EDV-No:281760' + '\n PSIT = Project Study IT-Systems EDV-No: 281765' + '\n PSAIS = Project Study Analytic Information Systems and Data Science EDV-No:281784' + '\n PSIM = Project Study Information Management EDV-No: 281780' + '\n PSSMU = Project Study Social Media in enterprise context' + '\n PSSMM = Project Study Social Media Management and Audiovisual Communication EDV-No:281796' + '\n PSSRM = Project Study Social Relationship Management EDV-No:281790') description = fields.Text('Description', required=True, help='Please enter a detailed ' + 'description of the topic \n Note: You can also add attachments') input_date = fields.Date('Date', readonly=True) like_count = fields.Integer('Like count', readonly=True) lecturer = fields.Many2One('party.party', 'Lecturer', required=True, select=True, ) mail = fields.Boolean('Mail to lecturer', states={ 'readonly': Not(Bool(Eval('lecturer'))), }, help='Select the lecturer which you want to assign') spo_selection = fields.Selection([ ('SPO3', 'SPO3'), ('SPO4', 'SPO4') ], 'SPO', help='Please select the SPO for which you will submit the proposal') @classmethod def default_like_count(cls): """Set the default value of the like_count field to 0. Because a new topic doesn't has likes""" return 0 @classmethod def default_input_date(cls): """Set the default value of the input_date fied to the current date""" return datetime.date.today() @classmethod def default_spo_selection(cls): """Set the default value of the spo_selection fied to SPO3""" return 'SPO3' @classmethod def default_project_study_spo3(cls): """Set the default value of the project_study_spo3 fied to None. This is necessary because this field is required and doesn't can be undefined""" return 'None' @classmethod def default_project_study_spo4(cls): """Set the default value of the project_study_spo4 fied to None. This is necessary because this field is required and doesn't can be undefined""" return 'None' @classmethod def default_like_state(cls): """Set the default value of the state field to false""" return 'like' @staticmethod def default_company(): return Transaction().context.get('company') def _get_like_btn(self): return {} def get_like_btn(self): return {} @classmethod def __setup__(cls): super(Hshn, cls).__setup__() """Initialize the like_btn""" cls._buttons.update({ 'like_btn': { } }) @fields.depends('spo_selection') def on_change_spo_selection(self): """Change the value of the fields project_study_spo3 and project_study_spo4 to '' and None depending on which SPO was selectet in the spo_selection field. This is necessary for the required fields logic""" if self.spo_selection == 'SPO3': self.project_study_spo3 = '' self.project_study_spo4 = 'None' else: self.project_study_spo4 = '' self.project_study_spo3 = 'None' @classmethod @ModelView.button def like_btn(cls, records): """Defines the logic by clicking on the Button. Count up the field like_count by one and save the changes into the database. It also insets a row in the table hshn_user to save which user liked which topic. This will be needed for deactivating the like button and activation the dislike button""" pool = Pool() model_hshn = pool.get('hshn.hshn') # get the current user ID user_id = Transaction().user model_user = pool.get('hshn.user') for row in records: # check if user already liked the topic user_list = model_user.search([('create_uid', '=', user_id)]) for user in user_list: if user.hshn_id is not None: if row.id is user.hshn_id.id: # topic already liked return # save the liked topic to the user values2 = [{'id':user_id, 'hshn_id':row.id}] model_user.create(values2) # count up the likes if row.like_count is None: row.like_count = 1 else: row.like_count += 1 # Save the updatet record model_hshn.save(records) return 'reload' @classmethod def validate(cls, records_mail): """Check before saving if the field mail is selected(True) and send in this case the entered values via mail to the lecturer. The email will be send to all email addresses which are saved for the lecturer in party.contact_mechanism""" super(Hshn, cls) pool = Pool() model = pool.get('party.contact_mechanism') try: server = get_smtp_server() except : pass # get the record row = records_mail # Read in the mail content mail_content = {} file = open('../trytond/modules/hshn/mail.txt') for line in file: try: name, var = line.partition('=') except ValueError: continue mail_content[name.strip()] = var return # send a mail if row.mail is True: # set the spo if row.spo_selection == 'SPO3': project_study = row.project_study_spo3 else: project_study = row.project_study_spo4 # get the party of the selected lecturer records_contact = model.search([('party', '=', row.lecturer)]) # send a mail to each mail address which is saved for the lecturer for row2 in records_contact: if row2.type == 'email': try: server.sendmail(mail_content['mail_address'], row2.value, mail_content['mail_message1'] + str(row.lecturer.name) + mail_content['mail_message2'] + '\n'+cls.topic.string+': ' + str(row.topic) + '\n'+cls.description.string+': ' + str(row.description) + '\n' + str(row.spo_selection) + mail_content['mail_message3'] + str(project_study)) except: pass # set mail to false row.mail = False
class Service(Workflow, ModelSQL, ModelView): 'Service' __name__ = 'service.service' __history = True company = fields.Many2One( 'company.company', 'Company', required=True, readonly=True, select=True, domain=[ ('id', If(Eval('context', {}).contains('company'), '=', '!='), Eval('context', {}).get('company', -1)), ], depends=_DEPENDS) party = fields.Many2One('party.party', 'Party', states=_STATES, required=True) number_service = fields.Char('No. Comprobante', readonly=True) type = fields.Selection(_TYPE, 'Type', select=True, states={ 'readonly': ((Eval('state') == 'delivered') | Eval('context', {}).get('type')), }) total = fields.Function( fields.Numeric('Total', states={ 'invisible': Eval('type') == 'home_service', }), 'get_amount') entry_date = fields.Date('Entry Date', states=_STATES, domain=[('entry_date', '<', Eval('delivery_date', None))], depends=['delivery_date']) delivery_date = fields.Date('Estimated Delivery Date', states=_STATES, domain=[('delivery_date', '>', Eval('entry_date', None))], depends=['entry_date']) technical = fields.Many2One('company.employee', 'Technical', states=_STATES) garanty = fields.Boolean('Garanty', help="Income Garanty", states=_STATES) new = fields.Boolean('New', states={ 'invisible': ~Eval('garanty', True), 'readonly': Eval('state') == 'delivered', }) lined = fields.Boolean('Lined', states={ 'invisible': ~Eval('garanty', True), 'readonly': Eval('state') == 'delivered', }) beaten = fields.Boolean('Beaten', states={ 'invisible': ~Eval('garanty', True), 'readonly': Eval('state') == 'delivered', }) broken = fields.Boolean('Broken', states={ 'invisible': ~Eval('garanty', True), 'readonly': Eval('state') == 'delivered', }) stained = fields.Boolean('Stained', states={ 'invisible': ~Eval('garanty', True), 'readonly': Eval('state') == 'delivered', }) invoice_date = fields.Date('Invoice Date', states={ 'invisible': ~Eval('garanty', True), 'readonly': Eval('state') == 'delivered', }) invoice_number = fields.Char('Invoice number', states={ 'invisible': ~Eval('garanty', True), 'readonly': Eval('state') == 'delivered', }) case_number = fields.Char('Case number', states={ 'invisible': ~Eval('garanty', True), 'readonly': Eval('state') == 'delivered', }) send_date = fields.Date('Send Date', states={ 'invisible': ~Eval('garanty', True), 'readonly': Eval('state') == 'delivered', }) remission = fields.Char('No. guide remission', states={ 'invisible': ~Eval('garanty', True), 'readonly': Eval('state') == 'delivered', }) transport = fields.Char('Transport', states={ 'invisible': ~Eval('garanty', True), 'readonly': Eval('state') == 'delivered', }) photo = fields.Binary('Foto', states=_STATES) state = fields.Selection([('pending', 'Pending'), ('review', 'In Review'), ('ready', 'Ready'), ('without', 'Without Solution'), ('warranty', 'Warranty not cover'), ('delivered', 'Delivered')], 'State', readonly=True) lines = fields.One2Many('service.service.line', 'service', 'Lines', states=_STATES) accessories = fields.Text('Accessories', states={ 'readonly': Eval('accessories') != '', }) observations = fields.Text('Observations', states=_STATES) history_lines = fields.One2Many('service.service.history_lines', 'service', 'Lines') total_home_service = fields.Numeric('Total', states={ 'invisible': Eval('type') == 'service', }) detail = fields.Text('Repair Detail', states={ 'invisible': Eval('state') != 'delivered', 'readonly': Eval('detail') != '', }) state_date = fields.Function(fields.Char('State Date'), 'get_state_date') equipo = fields.Char('Equipo') marca = fields.Char('Marca') modelo = fields.Char('Modelo') direccion = fields.Char(u'Dirección', states={ 'invisible': ~Eval('party'), }) telefono = fields.Char(u'Teléfono', states={ 'invisible': ~Eval('party'), }) correo = fields.Char(u'Correo Electrónico', states={ 'invisible': ~Eval('party'), }) @classmethod def __setup__(cls): super(Service, cls).__setup__() cls.__rpc__['getTechnicalService'] = RPC(check_access=False, readonly=False) cls._error_messages.update({ 'modify_invoice': ('You can not modify service "%s".'), 'delete_cancel': ('You can not delete service "%s".'), }) cls._transitions |= set(( ('pending', 'review'), ('review', 'ready'), ('review', 'without'), ('review', 'warranty'), ('ready', 'delivered'), ('without', 'delivered'), ('warranty', 'delivered'), )) cls._buttons.update({ 'review': { 'invisible': Eval('state') != ('pending') }, 'ready': { 'invisible': Eval('state').in_(['pending', 'ready', 'without', 'delivered']) }, 'without': { 'invisible': Eval('state').in_(['pending', 'ready', 'without', 'delivered']) }, 'warranty': { 'invisible': ~Eval('garanty', True) | (Eval('state').in_( ['pending', 'ready', 'without', 'delivered'])) }, 'delivered': { 'invisible': Eval('state').in_(['review', 'pending', 'delivered']) }, }) @fields.depends('invoice_date', 'garanty') def on_change_invoice_date(self): res = {} Date = Pool().get('ir.date') if self.garanty != None: year = Date.today() - datetime.timedelta(days=365) if self.invoice_date < year: res['invoice_date'] = self.invoice_date self.raise_user_error( u'Está seguro de la fecha de ingreso: "%s"' u'tiene mas de un año de garantia', (self.invoice_date)) else: res['invoice_date'] = self.invoice_date return res @fields.depends('party') def on_change_party(self): res = {} if self.party: if self.party.addresses[0]: res['direccion'] = self.party.addresses[0].street else: res['direccion'] = "" if self.party.phone: res['telefono'] = self.party.phone elif self.party.mobile: res['telefono'] = self.party.mobile else: res['telefono'] = "" if self.party.email: res['correo'] = self.party.email else: res['correo'] = "" else: res['direccion'] = "" res['telefono'] = "" res['correo'] = "" return res @fields.depends('lines') def on_change_lines(self): res = {} if self.lines: for line in self.lines: if line.product: res['equipo'] = line.periferic.name else: res['equipo'] = "" if line.trademark: res['marca'] = line.trademark.name else: res['marca'] = "" if line.model: res['modelo'] = line.model else: res['modelo'] = "" return res @classmethod def get_state_date(cls, services, names): pool = Pool() Date = pool.get('ir.date') date_now = Date.today() result = {n: {s.id: '' for s in services} for n in names} for name in names: for service in services: if (service.delivery_date < date_now) and (service.state != 'delivered'): result[name][service.id] = 'vencida' elif (service.delivery_date == date_now) and (service.state != 'delivered'): result[name][service.id] = 'vence_hoy' else: result[name][service.id] = '' return result @staticmethod def default_entry_date(): Date = Pool().get('ir.date') return Date.today() @staticmethod def default_accessories(): return '' @staticmethod def default_detail(): return '' @staticmethod def default_delivery_date(): Date = Pool().get('ir.date') return Date.today() + datetime.timedelta(days=1) @staticmethod def default_state(): return 'pending' @staticmethod def default_type(): return Transaction().context.get('type', 'service') @staticmethod def default_company(): return Transaction().context.get('company') @classmethod def get_amount(cls, services, names): amount = Decimal(0.0) total = dict((i.id, _ZERO) for i in services) for service in services: for line in service.lines: if line.reference_amount: amount += line.reference_amount total[service.id] = amount result = { 'total': total, } for key in result.keys(): if key not in names: del result[key] return result @fields.depends('total', 'total_home_service') def on_change_total_home_service(self): res = {} if self.total_home_service: res['total'] = self.total_home_service else: res['total'] = Decimal(0.0) return res def set_number(self): pool = Pool() Period = pool.get('account.period') Sequence = pool.get('ir.sequence.strict') Date = pool.get('ir.date') if self.number_service: return test_state = True accounting_date = self.entry_date period_id = Period.find(self.company.id, date=accounting_date, test_state=test_state) period = Period(period_id) sequence = period.get_service_sequence(self.type) if not sequence: self.raise_user_error('no_withholding_sequence', { 'withholding': self.rec_name, 'period': period.rec_name, }) with Transaction().set_context(date=self.entry_date or Date.today()): number = Sequence.get_id(sequence.id) vals = {'number_service': number} if (not self.entry_date and self.type in ('service')): vals['entry_date'] = Transaction().context['date'] self.write([self], vals) @classmethod def check_modify(cls, services): for service in services: if (service.state in ('delivered')): cls.raise_user_error('modify_invoice', (service.number_service, )) @classmethod def delete(cls, services): cls.check_modify(services) for service in services: if (service.state in ('review', 'ready', 'without', 'warranty', 'delivered')): cls.raise_user_error('delete_cancel', (service.number_service, )) super(Service, cls).delete(services) @classmethod def copy(cls, services, default=None): if default is None: default = {} default = default.copy() default['state'] = 'pending' default['number_service'] = None default['type'] = 'service' return super(Service, cls).copy(services, default=default) @classmethod @ModelView.button @Workflow.transition('review') def review(cls, services): for service in services: pool = Pool() Contact = pool.get('party.contact_mechanism') Address = pool.get('party.address') if service.party: if service.party.addresses[0].street: addresses = service.party.addresses[0] addresses.street = service.direccion addresses.save() else: if service.direccion: party = service.party party.address = Address.create([{ 'street': service.direccion, 'party': service.party.id }]) party.save() else: service.raise_user_error( u'Actualice la dirección del cliente') if service.party.email: emails = Contact.search([('party', '=', service.party), ('type', '=', 'email')]) for email in emails: email.value = service.correo email.save() else: if service.correo: contact_mechanisms = [] contact_mechanisms.append({ 'type': 'email', 'value': service.correo, 'party': service.party.id }) contact_mechanisms = Contact.create(contact_mechanisms) else: service.raise_user_error( 'Actualice el correo del cliente') if service.party.phone: phones = Contact.search([('party', '=', service.party), ('type', '=', 'phone')]) for phone in phones: phone.value = service.telefono phone.save() else: if service.telefono: contact_mechanisms = [] contact_mechanisms.append({ 'type': 'phone', 'value': service.telefono, 'party': service.party.id, }) contact_mechanisms = Contact.create(contact_mechanisms) else: service.raise_user_error( u'Actualice el teléfono del cliente') service.raise_user_warning( 'datos%s' % service.id, u'Está seguro que los datos del cliente:\n "%s"' u'\nCorreo: "%s" y Teléfono: "%s", \nestán actualizados.', (service.party.name, service.correo, service.telefono)) service.set_number() cls.write([i for i in services if i.state != 'review'], { 'state': 'review', }) @classmethod @ModelView.button @Workflow.transition('ready') def ready(cls, services): cls.write([i for i in services if i.state != 'ready'], { 'state': 'ready', }) @classmethod @ModelView.button @Workflow.transition('without') def without(cls, services): cls.write([i for i in services if i.state != 'without'], { 'state': 'without', }) @classmethod @ModelView.button @Workflow.transition('warranty') def warranty(cls, services): cls.write([i for i in services if i.state != 'warranty'], { 'state': 'warranty', }) @classmethod @ModelView.button @Workflow.transition('delivered') def delivered(cls, services): cls.write([i for i in services if i.state != 'delivered'], { 'state': 'delivered', }) @classmethod def getTechnicalService(cls, identificacion): pool = Pool() Service = pool.get('service.service') Party = pool.get('party.party') parties = Party.search([('vat_number', '=', identificacion)]) for p in parties: party = p services = Service.search([('party', '=', party)]) all_services = [] if services: for service in services: lines_services = {} for line in service.lines: lines_services[0] = str(service.entry_date) lines_services[1] = str(service.delivery_date) lines_services[2] = service.number_service lines_services[3] = line.periferic.name lines_services[4] = line.trademark.name lines_services[5] = line.model lines_services[6] = line.failure lines_services[7] = str(line.reference_amount) lines_services[8] = line.technical.party.name lines_services[9] = service.state lines_services[10] = service.accessories lines_services[11] = service.detail all_services.append(lines_services) return all_services else: return []
class OpenChartAccountStart(ModelView): 'Open Chart of Accounts' __name__ = 'analytic_account.open_chart.start' start_date = fields.Date('Start Date') end_date = fields.Date('End Date')
class BalanceProductFixedAssets(ModelSQL, ModelView): "Turnover and Balances Fixed Assets" _name = "ekd.balances.fixed_assets" _description =__doc__ _inherits = {'ekd.balances.assets': 'assets'} assets = fields.Many2One('ekd.balances.assets', 'Assets', required=True, ondelete='CASCADE') date_income = fields.Date('Date Income', required=True) date_expense = fields.Date('Date Expense') period_income = fields.Many2One('ekd.period', 'Period Income', required=True, select=2, domain=[ ('company','=',Eval('company')) ]) period_expense = fields.Many2One('ekd.period', 'Period Expense', select=2, domain=[ ('company','=',Eval('company')) ]) initial_cost = fields.Numeric('Initial cost', digits=(16, Eval('currency_digits', 2))) replacement = fields.Numeric('Replacement value', digits=(16, Eval('currency_digits', 2))) residual = fields.Numeric('Residual value', digits=(16, Eval('currency_digits', 2))) deprecation = fields.Many2Many("ekd.balances.fixed_assets.deprecation", 'fixed_assets', 'move_line', 'Deprecation') state = fields.Selection([ ('draft','Draft'), ('open','Open'), ('done','Closed'), ('deleted','Deleted') ], 'State', required=True) deleted = fields.Boolean('Flag Deleting') def __init__(self): super(BalanceProductFixedAssets, self).__init__() def default_state(self): return Transaction().context.get('state') or 'draft' def default_company(self): return Transaction().context.get('company') or False def default_currency_digits(self): return 2 def get_rec_name(self, ids, name): if not ids: return {} res = {} for balance in self.browse(ids): res[balance.id] = balance.product.name return res def get_currency_digits(self, ids, name): res = {}.fromkeys(ids, 2) company_id = Transaction().context.get('company') company = self.pool.get('company.company').browse(company_id) for line in self.browse(ids): if line.account.currency: res[line.id] = line.account.currency.currency_digits elif company.currency: res[line.id] = company.currency.currency_digits
class Mandate(Workflow, ModelSQL, ModelView): 'SEPA Mandate' __name__ = 'account.payment.sepa.mandate' party = fields.Many2One('party.party', 'Party', required=True, select=True, states={ 'readonly': Eval('state').in_( ['requested', 'validated', 'canceled']), }, depends=['state']) account_number = fields.Many2One( 'bank.account.number', 'Account Number', ondelete='RESTRICT', states={ 'readonly': Eval('state').in_(['validated', 'canceled']), 'required': Eval('state') == 'validated', }, domain=[ ('type', '=', 'iban'), ('account.owners', '=', Eval('party')), ], depends=['state', 'party']) identification = fields.Char('Identification', size=35, states={ 'readonly': Eval('identification_readonly', True), 'required': Eval('state') == 'validated', }, depends=['state', 'identification_readonly']) identification_readonly = fields.Function( fields.Boolean('Identification Readonly'), 'get_identification_readonly') company = fields.Many2One( 'company.company', 'Company', required=True, select=True, domain=[ ('id', If(Eval('context', {}).contains('company'), '=', '!='), Eval('context', {}).get('company', -1)), ], states={ 'readonly': Eval('state') != 'draft', }, depends=['state']) type = fields.Selection([ ('recurrent', 'Recurrent'), ('one-off', 'One-off'), ], 'Type', states={ 'readonly': Eval('state').in_(['validated', 'canceled']), }, depends=['state']) sequence_type_rcur = fields.Boolean("Always use RCUR", states={ 'invisible': Eval('type') == 'one-off', }, depends=['type']) scheme = fields.Selection([ ('CORE', 'Core'), ('B2B', 'Business to Business'), ], 'Scheme', required=True, states={ 'readonly': Eval('state').in_(['validated', 'canceled']), }, depends=['state']) scheme_string = scheme.translated('scheme') signature_date = fields.Date('Signature Date', states={ 'readonly': Eval('state').in_( ['validated', 'canceled']), 'required': Eval('state') == 'validated', }, depends=['state']) state = fields.Selection([ ('draft', 'Draft'), ('requested', 'Requested'), ('validated', 'Validated'), ('canceled', 'Canceled'), ], 'State', readonly=True) payments = fields.One2Many('account.payment', 'sepa_mandate', 'Payments') has_payments = fields.Function(fields.Boolean('Has Payments'), 'has_payments') @classmethod def __setup__(cls): super(Mandate, cls).__setup__() cls._transitions |= set(( ('draft', 'requested'), ('requested', 'validated'), ('validated', 'canceled'), ('requested', 'canceled'), ('requested', 'draft'), )) cls._buttons.update({ 'cancel': { 'invisible': ~Eval('state').in_(['requested', 'validated']), 'depends': ['state'], }, 'draft': { 'invisible': Eval('state') != 'requested', 'depends': ['state'], }, 'request': { 'invisible': Eval('state') != 'draft', 'depends': ['state'], }, 'validate_mandate': { 'invisible': Eval('state') != 'requested', 'depends': ['state'], }, }) t = cls.__table__() cls._sql_constraints = [ ('identification_unique', Unique(t, t.company, t.identification), 'account_payment_sepa.msg_mandate_unique_id'), ] @staticmethod def default_company(): return Transaction().context.get('company') @staticmethod def default_type(): return 'recurrent' @classmethod def default_sequence_type_rcur(cls): return False @staticmethod def default_scheme(): return 'CORE' @staticmethod def default_state(): return 'draft' @staticmethod def default_identification_readonly(): pool = Pool() Configuration = pool.get('account.configuration') config = Configuration(1) return bool(config.sepa_mandate_sequence) def get_identification_readonly(self, name): return bool(self.identification) def get_rec_name(self, name): if self.identification: return self.identification return '(%s)' % self.id @classmethod def search_rec_name(cls, name, clause): return [tuple(('identification', )) + tuple(clause[1:])] @classmethod def create(cls, vlist): pool = Pool() Sequence = pool.get('ir.sequence') Configuration = pool.get('account.configuration') config = Configuration(1) vlist = [v.copy() for v in vlist] for values in vlist: if (config.sepa_mandate_sequence and not values.get('identification')): values['identification'] = Sequence.get_id( config.sepa_mandate_sequence.id) # Prevent raising false unique constraint if values.get('identification') == '': values['identification'] = None return super(Mandate, cls).create(vlist) @classmethod def write(cls, *args): actions = iter(args) args = [] for mandates, values in zip(actions, actions): # Prevent raising false unique constraint if values.get('identification') == '': values = values.copy() values['identification'] = None args.extend((mandates, values)) super(Mandate, cls).write(*args) @classmethod def copy(cls, mandates, default=None): if default is None: default = {} else: default = default.copy() default.setdefault('payments', []) default.setdefault('signature_date', None) default.setdefault('identification', None) return super(Mandate, cls).copy(mandates, default=default) @property def is_valid(self): if self.state == 'validated': if self.type == 'one-off': if not self.has_payments: return True else: return True return False @property def sequence_type(self): if self.type == 'one-off': return 'OOFF' elif not self.sequence_type_rcur and (not self.payments or all( not p.sepa_mandate_sequence_type for p in self.payments) or all(p.rejected for p in self.payments)): return 'FRST' # TODO manage FNAL else: return 'RCUR' @classmethod def has_payments(cls, mandates, name): pool = Pool() Payment = pool.get('account.payment') payment = Payment.__table__ cursor = Transaction().connection.cursor() has_payments = dict.fromkeys([m.id for m in mandates], False) for sub_ids in grouped_slice(mandates): red_sql = reduce_ids(payment.sepa_mandate, sub_ids) cursor.execute(*payment.select(payment.sepa_mandate, Literal(True), where=red_sql, group_by=payment.sepa_mandate)) has_payments.update(cursor.fetchall()) return {'has_payments': has_payments} @classmethod @ModelView.button @Workflow.transition('draft') def draft(cls, mandates): pass @classmethod @ModelView.button @Workflow.transition('requested') def request(cls, mandates): pass @classmethod @ModelView.button @Workflow.transition('validated') def validate_mandate(cls, mandates): pass @classmethod @ModelView.button @Workflow.transition('canceled') def cancel(cls, mandates): # TODO must be automaticaly canceled 13 months after last collection pass @classmethod def delete(cls, mandates): for mandate in mandates: if mandate.state not in ('draft', 'canceled'): raise AccessError( gettext( 'account_payment_sepa' '.msg_mandate_delete_draft_canceled', mandate=mandate.rec_name)) super(Mandate, cls).delete(mandates)
class CompensationMoveStart(ModelView, BankMixin): 'Create Compensation Move Start' __name__ = 'account.move.compensation_move.start' party = fields.Many2One('party.party', 'Party', readonly=True) account = fields.Many2One('account.account', 'Account', domain=[ 'OR', ('type.receivable', '=', True), ('type.payable', '=', True) ], required=True) date = fields.Date('Date') maturity_date = fields.Date('Maturity Date') description = fields.Char('Description') payment_kind = fields.Selection([ ('both', 'Both'), ('payable', 'Payable'), ('receivable', 'Receivable'), ], 'Payment Kind') payment_type = fields.Many2One('account.payment.type', 'Payment Type', domain=[('kind', '=', Eval('payment_kind')) ], depends=['payment_kind']) @staticmethod def default_date(): pool = Pool() return pool.get('ir.date').today() @staticmethod def default_maturity_date(): pool = Pool() return pool.get('ir.date').today() @classmethod def default_get(cls, fields, with_rec_name=True): pool = Pool() Line = pool.get('account.move.line') PaymentType = pool.get('account.payment.type') defaults = super(CompensationMoveStart, cls).default_get(fields, with_rec_name) party = None company = None amount = Decimal('0.0') lines = Line.browse(Transaction().context.get('active_ids', [])) for line in lines: amount += line.debit - line.credit if not party: party = line.party elif party != line.party: raise UserError( gettext('account_bank.different_parties', party=line.party.rec_name, line=line.rec_name)) if not company: company = line.account.company if (company and company.currency.is_zero(amount) and len(set([x.account for x in lines])) == 1): raise UserError(gettext('account_bank.normal_reconcile')) if amount > 0: defaults['payment_kind'] = 'receivable' else: defaults['payment_kind'] = 'payable' defaults['bank_account'] = None if party: defaults['party'] = party.id if (defaults['payment_kind'] in ['receivable', 'both'] and party.customer_payment_type): defaults['payment_type'] = party.customer_payment_type.id elif (defaults['payment_kind'] in ['payable', 'both'] and party.supplier_payment_type): defaults['payment_type'] = party.supplier_payment_type.id if defaults.get('payment_type'): payment_type = PaymentType(defaults['payment_type']) defaults['account_bank'] = payment_type.account_bank self = cls() self.payment_type = payment_type self.party = party self._get_bank_account() defaults['account_bank_from'] = ( self.on_change_with_account_bank_from()) defaults['bank_account'] = (self.bank_account.id if self.bank_account else None) if amount > 0: defaults['account'] = (party.account_receivable.id if party.account_receivable else None) else: defaults['account'] = (party.account_payable.id if party.account_payable else None) return defaults def on_change_with_payment_type(self, name=None): pass
class Document(ModelSQL, ModelView): "Documents" _name='ekd.document' _order_name = 'date_document' _order=['date_document', 'date_account'] _description=__doc__ company = fields.Many2One('company.company', 'Company', readonly=True) model = fields.Many2One('ir.model', 'Model', domain=[('model','like','ekd.document%')]) model_str = fields.Char('Model Name', size=128) direction = fields.Selection([ ('input','Input'), ('move','Move'), ('internal','Internal'), ('output','Output') ],'Direction document') name = fields.Char('Description') template = fields.Many2One('ekd.document.template', 'Document Name', help="Template documents", order_field="%(table)s.template %(order)s") note = fields.Text('Note document') number_our = fields.Char('Number Outgoing', size=32, readonly=True, states={ 'invisible': Not(Bool(Eval('number_our'))) }) number_in = fields.Char('Number of incoming', size=32) date_document = fields.Date('Date Create') date_account = fields.Date('Date Account') employee = fields.Many2One('company.employee', 'Employee') from_party = fields.Many2One('party.party', 'From partner') to_party = fields.Many2One('party.party', 'To partner') amount = fields.Numeric('Amount Document', digits=(16, Eval("currency_digits",2))) amount_payment = fields.Function(fields.Numeric('Amount Payment', digits=(16, Eval("currency_digits", 2))), 'get_payment_field') amount_paid = fields.Function(fields.Numeric('Amount Paid', digits=(16, Eval("currency_digits", 2))), 'get_paid_field') currency = fields.Many2One('currency.currency', 'Currency') currency_digits = fields.Function(fields.Integer('Currency Digits' , on_change_with=['currency']), 'get_currency_digits') document_base = fields.Reference('Document Base', selection='documents_base_get', on_change=['document_base', 'lines']) lines = fields.One2Many('ekd.document.line.product', 'invoice', 'Lines') childs = fields.One2Many('ekd.document','parent', 'Child documents', readonly=True) parent = fields.Many2One('ekd.document', 'Parent document') state = fields.Selection(_STATES_FULL, 'State', readonly=True) stage = fields.Many2One('ekd.document.template.stage', 'Stage', readonly=True) journal_work = fields.One2Many('ekd.document.journal_work','document','Journal Change Stages', readonly=True) post_date = fields.Date('Date Post') active = fields.Boolean('Active', required=True) id_1c = fields.Char("ID import from 1C", size=None, select=1) deleting = fields.Boolean('Flag Deleting') def __init__(self): super(Document, self).__init__() self._order.insert(0, ('company','ASC')) self._order.insert(0, ('date_document', 'ASC')) self._order.insert(0, ('template', 'ASC')) self._order.insert(0, ('date_account', 'ASC')) self._order.insert(0, ('number_our', 'ASC')) def default_state(self): return 'draft' def default_date_document(self): context = Transaction().context if context.get('date_document'): return context.get('date_document') elif context.get('current_date'): return context.get('current_date') return datetime.datetime.now() def default_date_account(self ): context = Transaction().context if context.get('date_account'): return context.get('date_account') elif context.get('current_date'): return context.get('current_date') return datetime.datetime.now() def default_from_party(self): return Transaction().context.get('from_party') def default_to_party(self): return Transaction().context.get('to_party') def default_company(self ): return Transaction().context.get('company') def default_child(self): return Transaction().context.get('child') def default_amount(self): return Transaction().context.get('amount') def default_name(self): return Transaction().context.get('name') def default_note(self): return Transaction().context.get('note') def default_currency(self): company_obj = self.pool.get('company.company') currency_obj = self.pool.get('currency.currency') context = Transaction().context if context.get('company'): company = company_obj.browse(context['company']) return company.currency.id return False def default_currency_digits(self): company_obj = self.pool.get('company.company') context = Transaction().context if context.get('company'): company = company_obj.browse(context['company']) return company.currency.digits return 2 def default_active(self): return True def documents_base_dict_get(self, model=False): if not model: model = self._name dictions_obj = self.pool.get('ir.dictions') res = [] diction_ids = dictions_obj.search( [ ('model', 'like', 'ekd.document.head%'), ('pole', '=', 'document_base'), ]) if diction_ids: for diction in dictions_obj.browse( diction_ids): res.append([diction.key, diction.value]) return res def documents_base_get(self): return self.documents_base_dict_get(self._name) def get_rec_name(self, ids, name): res={} for document in self.browse(ids): if document.template: if document.template.shortcut: TemplateName = document.template.shortcut else: TemplateName = document.template.name if document.number_our: DocumentNumber = document.number_our elif document.number_in: DocumentNumber = document.number_in else: DocumentNumber = u'без номера' if document.date_document: DocumentDate = document.date_document.strftime('%d.%m.%Y') else: DocumentDate = u'без даты' if document.template: res[document.id] = TemplateName+ u' № '+DocumentNumber+u' от '+DocumentDate else: res[document.id] = u'Без имени № '+ DocumentNumber+u' от '+DocumentDate return res def get_payment_field(self, ids, name): res = {} cursor = Transaction().cursor for id in ids: res.setdefault(id, Decimal('0.0')) cursor.execute("SELECT doc_base, SUM(amount_payment) "\ "FROM ekd_document_line_payment "\ "WHERE doc_base in ("+','.join(map(str,ids))+") and state in ('wait0', 'wait1', 'payment') "\ "GROUP BY doc_base") for id, sum in cursor.fetchall(): # SQLite uses float for SUM if not isinstance(sum, Decimal): sum = Decimal(str(sum)) res[id] = sum return res def get_paid_field(self, ids, name): res = {} cursor = Transaction().cursor for id in ids: res.setdefault(id, Decimal('0.0')) cursor.execute("SELECT doc_base, SUM(amount_payment) "\ "FROM ekd_document_line_payment "\ "WHERE doc_base in ("+','.join(map(str,ids))+") and state='done' "\ "GROUP BY doc_base") for id, sum in cursor.fetchall(): # SQLite uses float for SUM if not isinstance(sum, Decimal): sum = Decimal(str(sum)) res[id] = sum return res def get_currency_digits(self, ids, name): assert name in ('currency_digits'), 'Invalid name %s' % (name) res={}.fromkeys(ids, 2) for document in self.browse(ids): if document.currency: res[document.id] = document.currency.digits return res def on_change_document_base(self, values): return values def button_draft(self, ids): for document in self.browse(ids): if document.template.model: self.pool.get(document.template.model).button_draft(ids) def button_cancel(self, ids): for document in self.browse(ids): if document.template.model: self.pool.get(document.template.model).button_cancel(ids) def button_restore(self, ids): for document in self.browse(ids): if document.template.model: self.pool.get(document.template.model).button_restore(ids) def button_issued(self, ids): for document in self.browse(idst): if document.template.model: self.pool.get(document.template.model).button_issued(ids) def button_on_payment(self, ids): for document in self.browse(idst): if document.template.model: self.pool.get(document.template.model).button_payment(ids) def button_pay(self, ids): for document in self.browse(idst): if document.template.model: self.pool.get(document.template.model).button_pay(ids) def button_confirmed(self, ids): for document in self.browse(idst): if document.template.model: self.pool.get(document.template.model).button_confirmed(ids)
class HrPayslip(Workflow, ModelSQL, ModelView): '''Pay Slip''' __name__ = 'hr.payslip' employee = fields.Many2One( 'company.employee', 'Employee', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state']) salary_code = fields.Char( 'Salary Code', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state']) structure = fields.Many2One( 'salary.structure', 'Structure', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state']) contract = fields.Many2One( 'hr.contract', 'Salary Details', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state']) code = fields.Integer('Code', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state']) name = fields.Char('name', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state']) number = fields.Char('Reference', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state']) date_from = fields.Date('Date From', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state']) date_to = fields.Date('Date To', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state']) state = fields.Selection([('draft', 'Draft'), ('verify', 'Waiting'), ('done', 'Done'), ('cancel', 'Rejected'), ('confirm', 'Successful')], 'Status', readonly=True) lines = fields.One2Many('hr.payslip.line', 'slip', string='Payslip Lines', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state']) pay_and_allowance = fields.One2Many( 'hr.payslip.line', 'slip', string='Payslip Lines', domain=[('category.code', 'in', ['Basic', 'Allowance', 'Gross', 'Net'])], states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state']) deductions = fields.One2Many( 'hr.payslip.line', 'slip', string='Payslip Lines', domain=[('category.code', 'in', ['Deduction'])], states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state']) details_by_salary_rule_category = fields.One2Many( 'hr.payslip.line', 'slip', string='Details by Salary Rule', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state']) payslip_run = fields.Many2One( 'hr.payslip.run', 'Payslip Batches', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state']) #batches in payslip.run bank_name = fields.Char('Bank Name', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state'], readonly=True) ifsc = fields.Char('IFSC Code', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state'], readonly=True) bank_address = fields.Text( 'Bank Address', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state'], readonly=True) account_no = fields.Integer( 'Account Number', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state'], readonly=True) bank_status = fields.Char( 'Bank Account Status', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state'], readonly=True) @fields.depends('employee') def on_change_employee(self): self.salary_code = self.employee.salary_code self.number = self self.bank_name = self.employee.bank_name.bank self.bank_address = self.employee.bank_address self.account_no = self.employee.account_no self.bank_status = self.employee.bank_status start_date = datetime.date.today() contract = Pool().get('hr.contract') contracts = contract.search([('employee', '=', self.employee), ('date_start', '<=', start_date), ('date_end', '>=', start_date)]) @property def pay_and_allowances(self): allowances_lines = [] if self.lines: for line in self.lines: if line.category.code in ['BASIC', 'ALW']: allowances_lines.append(line) return allowances_lines @property def deductions(self): deductions_lines = [] if self.lines: for line in self.lines: if line.category.code in ['DED']: deductions_lines.append(line) return deductions_lines @staticmethod def default_state(): return 'draft' @classmethod def __setup__(cls): super().__setup__() cls._order = [ ('salary_code', 'ASC'), ] cls._transitions |= set(( ('draft', 'verify'), ('verify', 'confirm'), ('confirm', 'done'), ('confirm', 'cancel'), ('verify', 'cancel'), )) cls._buttons.update({ 'verify': { 'invisible': ~Eval('state').in_(['draft']), 'depends': ['state'], }, 'cancel': { 'invisible': ~Eval('state').in_(['confirm', 'verify']), 'depends': ['state'], }, 'confirm': { 'invisible': ~Eval('state').in_(['verify']), 'depends': ['state'], }, 'done': { 'invisible': ~Eval('state').in_(['confirm']), 'depends': ['state'], }, }) @classmethod @ModelView.button @Workflow.transition('verify') def verify(cls, records): pass @classmethod @ModelView.button @Workflow.transition('cancel') def cancel(cls, records): pass @classmethod @ModelView.button @Workflow.transition('confirm') def confirm(cls, records): pass @classmethod @ModelView.button @Workflow.transition('done') def done(cls, records): pass def calculate_rules(self): structure = self.structure PayslipLine = Pool().get('hr.payslip.line') for rule in structure.rules: condition = rule.check( self, self.employee, self.contract, ) if not condition: continue amount = rule.calculate( self, self.employee, self.contract, ) vals = { 'name': rule.name, 'code': rule.code, 'slip': self.id, 'salary_rule': rule.id, 'category': rule.category.id, 'employee': self.employee.id, 'amount': amount, 'total': amount, 'priority': rule.priority } print(vals) line = PayslipLine.create([vals]) @classmethod #@ModelView.button_action('payroll.payslip_line_view_tree') def _compute_salary(cls, records): pass @classmethod def default_date_from(cls): start_date = datetime.date.today().replace(day=1) # y=datetime.datetime(x.year,x.month,1) return start_date @classmethod def default_date_to(cls): today = datetime.date.today().month end_date = (datetime.date.today().replace(month=today + 1, day=1) - datetime.timedelta(days=1)) return end_date @classmethod def default_payslip_month_year(cls): month = datetime.date.today().strftime("%B") year = datetime.date.today().year date_ = month + ", " + str(year) return date_ @classmethod def validate(cls, records): super(HrPayslip, cls).validate(records) for record in records: if not record.salary_code: cls.raise_user_error('Enter Salary Code')
class DateRequired(ModelSQL): 'Date Required' __name__ = 'test.date_required' date = fields.Date(string='Date', help='Test date', required=True)
class ShipmentDrop(Workflow, ModelSQL, ModelView): "Drop Shipment" __name__ = 'stock.shipment.drop' effective_date = fields.Date('Effective Date', readonly=True) planned_date = fields.Date('Planned Date', states={ 'readonly': Eval('state') != 'draft', }, depends=['state']) company = fields.Many2One( 'company.company', 'Company', required=True, states={ 'readonly': Eval('state') != 'draft', }, domain=[ ('id', If(Eval('context', {}).contains('company'), '=', '!='), Eval('context', {}).get('company', -1)), ], depends=['state']) reference = fields.Char('Reference', select=1, states={ 'readonly': Eval('state') != 'draft', }, depends=['state']) supplier = fields.Many2One('party.party', 'Supplier', required=True, states={ 'readonly': (((Eval('state') != 'draft') | Eval('supplier_moves', [0])) & Eval('supplier')), }, depends=['state', 'supplier']) contact_address = fields.Many2One('party.address', 'Contact Address', states={ 'readonly': Eval('state') != 'draft', }, domain=[('party', '=', Eval('supplier')) ], depends=['state', 'supplier']) customer = fields.Many2One('party.party', 'Customer', required=True, states={ 'readonly': (((Eval('state') != 'draft') | Eval('customer_moves', [0])) & Eval('customer')), }, depends=['state']) delivery_address = fields.Many2One('party.address', 'Delivery Address', required=True, states={ 'readonly': Eval('state') != 'draft', }, domain=[('party', '=', Eval('customer')) ], depends=['state', 'customer']) moves = fields.One2Many('stock.move', 'shipment', 'Moves', domain=[ ('company', '=', Eval('company')), [ 'OR', [ ('from_location.type', '=', 'supplier'), ('to_location.type', '=', 'drop'), ], [ ('from_location.type', '=', 'drop'), ('to_location.type', '=', 'customer'), ], ], ], depends=['company'], readonly=True) supplier_moves = fields.One2Many( 'stock.move', 'shipment', 'Supplier Moves', filter=[('to_location.type', '=', 'drop')], states={ 'readonly': Eval('state').in_(['shipped', 'done', 'cancel']), }, depends=['state', 'supplier']) customer_moves = fields.One2Many('stock.move', 'shipment', 'Customer Moves', filter=[('from_location.type', '=', 'drop')], states={ 'readonly': Eval('state') != 'shipped', }, depends=['state', 'customer']) code = fields.Char('Code', select=1, readonly=True) state = fields.Selection([ ('draft', 'Draft'), ('waiting', 'Waiting'), ('shipped', 'Shipped'), ('done', 'Done'), ('cancel', 'Canceled'), ], 'State', readonly=True) @classmethod def __register__(cls, module_name): pool = Pool() Move = pool.get('stock.move') PurchaseLine = pool.get('purchase.line') PurchaseRequest = pool.get('purchase.request') SaleLine = pool.get('sale.line') Location = pool.get('stock.location') move = Move.__table__() purchase_line = PurchaseLine.__table__() purchase_request = PurchaseRequest.__table__() sale_line = SaleLine.__table__() location = Location.__table__() cursor = Transaction().connection.cursor() super(ShipmentDrop, cls).__register__(module_name) # Migration from 3.6 cursor.execute(*location.select(Count(location.id), where=(location.type == 'drop'))) has_drop_shipment, = cursor.fetchone() if not has_drop_shipment: drop_shipment = Location(name='Migration Drop Shipment', type='drop', active=False) drop_shipment.save() drop_shipment_location = drop_shipment.id move_sale_query = move.join( purchase_line, condition=move.origin == Concat('purchase.line,', purchase_line.id)).join( purchase_request, condition=purchase_request.purchase_line == purchase_line.id).join( sale_line, condition=sale_line.purchase_request == purchase_request.id).select( move.id, move.to_location, sale_line.id, where=move.shipment.like('stock.shipment.drop,%')) cursor.execute(*move_sale_query) move_sales = cursor.fetchall() for sub_move in grouped_slice(move_sales): sub_ids = [s[0] for s in sub_move] cursor.execute(*move.update(columns=[move.to_location], values=[drop_shipment_location], where=move.id.in_(sub_ids))) cursor.execute(*move.select(limit=1)) moves = list(cursor_dict(cursor)) if moves: move_columns = moves[0].keys() columns = [Column(move, c) for c in move_columns if c != 'id'] create_move = move.insert( columns=columns, values=move.select( *columns, where=move.shipment.like('stock.shipment.drop,%'))) cursor.execute(*create_move) for move_id, customer_location, line_id in move_sales: cursor.execute( *move.update(columns=[ move.origin, move.from_location, move.to_location ], values=[ Concat('sale.line,', str(line_id)), drop_shipment_location, customer_location ], where=(move.id == move_id))) @classmethod def __setup__(cls): super(ShipmentDrop, cls).__setup__() cls._transitions |= set(( ('draft', 'waiting'), ('waiting', 'shipped'), ('draft', 'cancel'), ('waiting', 'cancel'), ('waiting', 'draft'), ('cancel', 'draft'), ('shipped', 'done'), ('shipped', 'cancel'), )) cls._buttons.update({ 'cancel': { 'invisible': Eval('state').in_(['cancel', 'done']), 'depends': ['state'], }, 'draft': { 'invisible': ~Eval('state').in_(['cancel', 'draft', 'waiting']), 'icon': If(Eval('state') == 'cancel', 'tryton-undo', 'tryton-back'), 'depends': ['state'], }, 'wait': { 'invisible': Eval('state') != 'draft', 'depends': ['state'], }, 'ship': { 'invisible': Eval('state') != 'waiting', 'depends': ['state'], }, 'done': { 'invisible': Eval('state') != 'shipped', 'depends': ['state'], }, }) @staticmethod def default_state(): return 'draft' @staticmethod def default_company(): return Transaction().context.get('company') @fields.depends('supplier') def on_change_supplier(self): if self.supplier: self.contact_address = self.supplier.address_get() else: self.contact_address = None @fields.depends('customer') def on_change_customer(self): if self.customer: self.delivery_address = self.customer.address_get(type='delivery') else: self.delivery_address = None def _get_move_planned_date(self): ''' Return the planned date for moves ''' return self.planned_date @classmethod def _set_move_planned_date(cls, shipments): ''' Set planned date of moves for the shipments ''' Move = Pool().get('stock.move') to_write = [] for shipment in shipments: planned_date = shipment._get_move_planned_date() to_write.extend(([ m for m in shipment.moves if m.state not in ('assigned', 'done', 'cancel') ], { 'planned_date': planned_date, })) if to_write: Move.write(*to_write) @classmethod def create(cls, vlist): pool = Pool() Sequence = pool.get('ir.sequence') Config = pool.get('stock.configuration') vlist = [x.copy() for x in vlist] config = Config(1) for values in vlist: values['code'] = Sequence.get_id(config.shipment_drop_sequence) shipments = super(ShipmentDrop, cls).create(vlist) cls._set_move_planned_date(shipments) return shipments @classmethod def write(cls, *args): super(ShipmentDrop, cls).write(*args) cls._set_move_planned_date(sum(args[::2], [])) @classmethod def copy(cls, shipments, default=None): if default is None: default = {} else: default = default.copy() default.setdefault('moves', None) return super(ShipmentDrop, cls).copy(shipments, default=default) @classmethod def delete(cls, shipments): pool = Pool() Move = pool.get('stock.move') cls.cancel(shipments) for shipment in shipments: if shipment.state != 'cancel': raise AccessError( gettext('sale_supply_drop_shipment' '.msg_drop_shipment_delete_cancel') % { 'shipment': shipment.rec_name, }) Move.delete([m for s in shipments for m in s.supplier_moves]) super(ShipmentDrop, cls).delete(shipments) @classmethod @ModelView.button @Workflow.transition('cancel') @process_sale('customer_moves') @process_purchase('supplier_moves') def cancel(cls, shipments): Move = Pool().get('stock.move') Move.cancel([m for s in shipments for m in s.supplier_moves]) Move.cancel([ m for s in shipments for m in s.customer_moves if s.state == 'shipped' ]) Move.write([ m for s in shipments for m in s.customer_moves if s.state != 'shipped' ], {'shipment': None}) @classmethod @ModelView.button @Workflow.transition('draft') def draft(cls, shipments): pool = Pool() Move = pool.get('stock.move') PurchaseLine = pool.get('purchase.line') SaleLine = pool.get('sale.line') for shipment in shipments: for move in shipment.moves: if (move.state == 'cancel' and isinstance(move.origin, (PurchaseLine, SaleLine))): raise AccessError( gettext('sale_supply_drop_shipment.msg_reset_move', move=move.rec_name)) Move.draft( [m for s in shipments for m in s.moves if m.state != 'staging']) @classmethod def _synchronize_moves(cls, shipments): pool = Pool() UoM = pool.get('product.uom') Move = pool.get('stock.move') to_save = [] cost_exp = Decimal(str(10.0**-Move.cost_price.digits[1])) for shipment in shipments: product_qty = defaultdict(int) product_cost = defaultdict(int) for c_move in shipment.customer_moves: if c_move.state == 'cancel': continue product_qty[c_move.product] += UoM.compute_qty( c_move.uom, c_move.quantity, c_move.product.default_uom, round=False) s_product_qty = defaultdict(int) for s_move in shipment.supplier_moves: if s_move.state == 'cancel': continue if s_move.cost_price: internal_quantity = Decimal(str(s_move.internal_quantity)) product_cost[s_move.product] += (s_move.unit_price * internal_quantity) quantity = UoM.compute_qty(s_move.uom, s_move.quantity, s_move.product.default_uom, round=False) s_product_qty[s_move.product] += quantity if product_qty[s_move.product]: if quantity <= product_qty[s_move.product]: product_qty[s_move.product] -= quantity continue else: out_quantity = (quantity - product_qty[s_move.product]) out_quantity = UoM.compute_qty( s_move.product.default_uom, out_quantity, s_move.uom) product_qty[s_move.product] = 0 else: out_quantity = s_move.quantity if not out_quantity: continue unit_price = UoM.compute_price(s_move.product.default_uom, s_move.product.list_price, s_move.uom) new_customer_move = shipment._get_customer_move(s_move) new_customer_move.quantity = out_quantity new_customer_move.unit_price = unit_price to_save.append(new_customer_move) for product, cost in product_cost.items(): qty = Decimal(str(s_product_qty[product])) if qty: product_cost[product] = (cost / qty).quantize(cost_exp) for c_move in list(shipment.customer_moves) + to_save: if c_move.id is not None and c_move.state == 'cancel': continue c_move.cost_price = product_cost[c_move.product] if c_move.id is None: continue if product_qty[c_move.product] > 0: exc_qty = UoM.compute_qty(c_move.product.default_uom, product_qty[c_move.product], c_move.uom) removed_qty = UoM.compute_qty(c_move.uom, min(exc_qty, c_move.quantity), c_move.product.default_uom, round=False) c_move.quantity = max( 0, c_move.uom.round(c_move.quantity - exc_qty)) product_qty[c_move.product] -= removed_qty to_save.append(c_move) if to_save: Move.save(to_save) def _get_customer_move(self, move): pool = Pool() Move = pool.get('stock.move') return Move( from_location=move.to_location, to_location=self.customer.customer_location, product=move.product, uom=move.uom, quantity=move.quantity, shipment=self, planned_date=self.planned_date, company=move.company, currency=move.company.currency, unit_price=move.unit_price, ) @classmethod @ModelView.button @Workflow.transition('waiting') def wait(cls, shipments): pool = Pool() PurchaseRequest = pool.get('purchase.request') SaleLine = pool.get('sale.line') Move = pool.get('stock.move') requests = [] for sub_lines in grouped_slice([ m.origin.id for s in shipments for m in s.supplier_moves if m.origin ]): requests += PurchaseRequest.search([ ('purchase_line', 'in', list(sub_lines)), ]) pline2request = {r.purchase_line: r for r in requests} sale_lines = SaleLine.search([ ('purchase_request', 'in', [r.id for r in requests]), ]) request2sline = {sl.purchase_request: sl for sl in sale_lines} to_save = [] for shipment in shipments: for move in shipment.supplier_moves: if not move.origin: continue sale_line = request2sline.get(pline2request.get(move.origin)) if not sale_line: continue for move in sale_line.moves: if (move.state not in ('cancel', 'done') and not move.shipment and move.from_location.type == 'drop'): move.shipment = shipment to_save.append(move) Move.save(to_save) cls._synchronize_moves(shipments) @classmethod @ModelView.button @Workflow.transition('shipped') @process_purchase('supplier_moves') def ship(cls, shipments): pool = Pool() Move = pool.get('stock.move') Move.do([m for s in shipments for m in s.supplier_moves]) cls._synchronize_moves(shipments) @classmethod @ModelView.button @Workflow.transition('done') @process_sale('customer_moves') def done(cls, shipments): pool = Pool() Move = pool.get('stock.move') Date = pool.get('ir.date') Move.do([m for s in shipments for m in s.customer_moves]) cls.write(shipments, { 'effective_date': Date.today(), })
class ProductQuantitiesByWarehouseMove(ModelSQL, ModelView): "Product Quantities By Warehouse Moves" __name__ = 'stock.product_quantities_warehouse.move' date = fields.Date("Date") move = fields.Many2One('stock.move', "Move") origin = fields.Reference("Origin", selection='get_origin') quantity = fields.Float("Quantity") cumulative_quantity_start = fields.Function( fields.Float("Cumulative Quantity Start"), 'get_cumulative_quantity') cumulative_quantity_delta = fields.Float("Cumulative Quantity Delta") cumulative_quantity_end = fields.Function( fields.Float("Cumulative Quantity End"), 'get_cumulative_quantity') company = fields.Many2One('company.company', "Company") @classmethod def __setup__(cls): super().__setup__() cls._order.insert(0, ('date', 'ASC')) @classmethod def table_query(cls): pool = Pool() Date = pool.get('ir.date') Location = pool.get('stock.location') Move = pool.get('stock.move') Product = pool.get('product.product') move = from_ = Move.__table__() transaction = Transaction() context = transaction.context database = transaction.database today = Date.today() if context.get('product_template') is not None: product = Product.__table__() from_ = move.join(product, condition=move.product == product.id) product_clause = product.template == context['product_template'] else: product_clause = move.product == context.get('product', -1) if 'warehouse' in context: warehouse = Location(context.get('warehouse')) if context.get('stock_skip_warehouse'): location_id = warehouse.storage_location.id else: location_id = warehouse.id else: location_id = -1 warehouse = With('id', query=Location.search([ ('parent', 'child_of', [location_id]), ], query=True, order=[])) date_column = Coalesce(move.effective_date, move.planned_date) quantity = Case((move.to_location.in_(warehouse.select( warehouse.id)), move.internal_quantity), else_=-move.internal_quantity) if database.has_window_functions(): cumulative_quantity_delta = Sum(quantity, window=Window( [date_column], order_by=[move.id.asc])) else: cumulative_quantity_delta = Literal(0) return (from_.select( move.id.as_('id'), Literal(0).as_('create_uid'), CurrentTimestamp().as_('create_date'), Literal(None).as_('write_uid'), Literal(None).as_('write_date'), date_column.as_('date'), move.id.as_('move'), move.origin.as_('origin'), quantity.as_('quantity'), cumulative_quantity_delta.as_('cumulative_quantity_delta'), move.company.as_('company'), where=product_clause & ((move.from_location.in_(warehouse.select(warehouse.id)) & ~move.to_location.in_(warehouse.select(warehouse.id))) | (~move.from_location.in_(warehouse.select(warehouse.id)) & move.to_location.in_(warehouse.select(warehouse.id)))) & ((date_column < today) & (move.state == 'done') | (date_column >= today)), with_=warehouse)) @classmethod def get_origin(cls): pool = Pool() Move = pool.get('stock.move') return Move.get_origin() @classmethod def get_cumulative_quantity(cls, records, names): pool = Pool() Product = pool.get('product.product') transaction = Transaction() database = transaction.database trans_context = transaction.context def valid_context(name): return (trans_context.get(name) is not None and isinstance(trans_context[name], int)) if not any(map(valid_context, ['product', 'product_template'])): return {r.id: None for r in records} if trans_context.get('product') is not None: grouping = ('product', ) grouping_filter = ([trans_context['product']], ) key = trans_context['product'] else: grouping = ('product.template', ) grouping_filter = ([trans_context.get('product_template')], ) key = trans_context['product_template'] warehouse_id = trans_context.get('warehouse') def cast_date(date): if isinstance(date, str): date = datetime.date(*map(int, date.split('-', 2))) return date dates = sorted({cast_date(r.date) for r in records}) quantities = {} date_start = None for date in dates: try: context = { 'stock_date_start': date_start, 'stock_date_end': date - datetime.timedelta(days=1), 'forecast': True, } except OverflowError: pass with Transaction().set_context(**context): quantities[date] = Product.products_by_location( [warehouse_id], grouping=grouping, grouping_filter=grouping_filter, with_childs=True).get((warehouse_id, key), 0) date_start = date cumulate = 0 for date in dates: cumulate += quantities[date] quantities[date] = cumulate result = {} if database.has_window_functions(): if 'cumulative_quantity_start' in names: result['cumulative_quantity_start'] = { r.id: (quantities[cast_date(r.date)] + r.cumulative_quantity_delta - r.quantity) for r in records } if 'cumulative_quantity_end' in names: result['cumulative_quantity_end'] = { r.id: (quantities[cast_date(r.date)] + r.cumulative_quantity_delta) for r in records } else: values = {r.id: quantities[cast_date(r.date)] for r in records} for name in names: result[name] = values return result def get_rec_name(self, name): return self.move.rec_name
class PurchaseRequisition(Workflow, ModelSQL, ModelView): "Purchase Requisition" __name__ = 'purchase.requisition' _rec_name = 'number' _states = { 'readonly': Eval('state') != 'draft', } company = fields.Many2One( 'company.company', "Company", required=True, select=True, states={ 'readonly': (Eval('state') != 'draft') | Eval('lines', [0]), }) number = fields.Char('Number', readonly=True, select=True) description = fields.Char('Description', states=_states) employee = fields.Many2One( 'company.employee', 'Employee', required=True, states=_states) supply_date = fields.Date( 'Supply Date', states={ 'required': ~Eval('state').in_(['draft', 'cancelled']), 'readonly': _states['readonly'], }) warehouse = fields.Many2One( 'stock.location', 'Warehouse', domain=[ ('type', '=', 'warehouse'), ], states=_states) currency = fields.Many2One( 'currency.currency', 'Currency', states={ 'readonly': (_states['readonly'] | (Eval('lines', [0]) & Eval('currency'))), }) total_amount = fields.Function( Monetary("Total", currency='currency', digits='currency'), 'get_amount') total_amount_cache = Monetary( "Total Cache", currency='currency', digits='currency') lines = fields.One2Many( 'purchase.requisition.line', 'requisition', 'Lines', states=_states) approved_by = employee_field( "Approved By", states=['approved', 'processing', 'done', 'cancelled']) rejected_by = employee_field( "Rejected By", states=['rejected', 'processing', 'done', 'cancelled']) state = fields.Selection([ ('draft', "Draft"), ('waiting', "Waiting"), ('rejected', "Rejected"), ('approved', "Approved"), ('processing', "Processing"), ('done', "Done"), ('cancelled', "Cancelled"), ], "State", readonly=True, required=True, sort=False) del _states @classmethod def __setup__(cls): super(PurchaseRequisition, cls).__setup__() cls._transitions |= set(( ('cancelled', 'draft'), ('rejected', 'draft'), ('draft', 'cancelled'), ('draft', 'waiting'), ('waiting', 'draft'), ('waiting', 'rejected'), ('waiting', 'approved'), ('approved', 'processing'), ('approved', 'draft'), ('processing', 'done'), ('done', 'processing'), )) cls._buttons.update({ 'cancel': { 'invisible': Eval('state') != 'draft', 'depends': ['state'], }, 'draft': { 'invisible': ~Eval('state').in_( ['cancelled', 'waiting', 'approved', 'rejected']), 'icon': If(Eval('state').in_(['cancelled', 'rejected']), 'tryton-undo', 'tryton-back'), 'depends': ['state'], }, 'wait': { 'pre_validate': [('supply_date', '!=', None)], 'invisible': ((Eval('state') != 'draft') | ~Eval('lines', [])), 'readonly': ~Eval('lines', []), 'depends': ['state'], }, 'approve': { 'invisible': Eval('state') != 'waiting', 'depends': ['state'], }, 'process': { 'invisible': ~Eval('state').in_( ['approved', 'processing']), 'icon': If(Eval('state') == 'approved', 'tryton-forward', 'tryton-refresh'), 'depends': ['state'], }, 'reject': { 'invisible': Eval('state') != 'waiting', 'depends': ['state'], }, }) # The states where amounts are cached cls._states_cached = ['approved', 'done', 'rejected', 'processing', 'cancelled'] @classmethod def __register__(cls, module_name): cursor = Transaction().connection.cursor() table = cls.__table__() super().__register__(module_name) # Migration from 5.6: rename state cancel to cancelled cursor.execute(*table.update( [table.state], ['cancelled'], where=table.state == 'cancel')) @classmethod def default_state(cls): return 'draft' @classmethod def default_company(cls): return Transaction().context.get('company') @classmethod def default_employee(cls): return Transaction().context.get('employee') @classmethod def default_warehouse(cls): Location = Pool().get('stock.location') return Location.get_default_warehouse() @classmethod def default_currency(cls): Company = Pool().get('company.company') company = Transaction().context.get('company') if company: return Company(company).currency.id @fields.depends('lines', 'currency') def on_change_with_total_amount(self): self.total_amount = Decimal('0.0') if self.lines: for line in self.lines: self.total_amount += getattr(line, 'amount', None) or 0 if self.currency: self.total_amount = self.currency.round(self.total_amount) return self.total_amount @classmethod def store_cache(cls, requisitions): for requisition in requisitions: requisition.total_amount_cache = requisition.total_amount cls.save(requisitions) @classmethod def get_amount(cls, requisitions, name): total_amount = {} # Sort cached first and re-instantiate to optimize cache management requisitions = sorted(requisitions, key=lambda r: r.state in cls._states_cached, reverse=True) requisitions = cls.browse(requisitions) for requisition in requisitions: if (requisition.state in cls._states_cached and requisition.total_amount_cache is not None): total_amount[requisition.id] = requisition.total_amount_cache else: total_amount[requisition.id] = ( requisition.on_change_with_total_amount()) return total_amount @classmethod def create_requests(cls, requisitions): pool = Pool() Request = pool.get('purchase.request') requests = [] for requisition in requisitions: for line in requisition.lines: request = line.compute_request() if request: requests.append(request) if requests: Request.save(requests) @classmethod def view_attributes(cls): return super().view_attributes() + [ ('/tree', 'visual', If(Eval('state') == 'cancelled', 'muted', '')), ] @classmethod def create(cls, vlist): pool = Pool() Config = pool.get('purchase.configuration') config = Config(1) vlist = [v.copy() for v in vlist] default_company = cls.default_company() for values in vlist: if values.get('number') is None: values['number'] = config.get_multivalue( 'purchase_requisition_sequence', company=values.get('company', default_company)).get() return super(PurchaseRequisition, cls).create(vlist) @classmethod def delete(cls, requisitions): # Cancel before delete cls.cancel(requisitions) for requisition in requisitions: if requisition.state != 'cancelled': raise AccessError( gettext('purchase_requisition.msg_delete_cancel', requisition=requisition.rec_name)) super(PurchaseRequisition, cls).delete(requisitions) def check_for_waiting(self): if not self.warehouse: for line in self.lines: if line.product and line.product.type in {'goods', 'assets'}: raise RequiredValidationError( gettext('purchase_requisition.msg_warehouse_required', requisition=self.rec_name)) @classmethod def copy(cls, requisitions, default=None): if default is None: default = {} else: default = default.copy() default.setdefault('number', None) default.setdefault('supply_date', None) default.setdefault('approved_by') default.setdefault('rejected_by') return super(PurchaseRequisition, cls).copy( requisitions, default=default) @classmethod @ModelView.button @Workflow.transition('cancelled') def cancel(cls, requisitions): cls.store_cache(requisitions) @classmethod @ModelView.button @Workflow.transition('draft') @reset_employee('approved_by', 'rejected_by') def draft(cls, requisitions): pass @classmethod @ModelView.button @Workflow.transition('waiting') def wait(cls, requisitions): for requisition in requisitions: requisition.check_for_waiting() @classmethod @ModelView.button @Workflow.transition('rejected') @set_employee('rejected_by') def reject(cls, requisitions): pass @classmethod @ModelView.button @Workflow.transition('approved') @set_employee('approved_by') def approve(cls, requisitions): pool = Pool() Configuration = pool.get('purchase.configuration') transaction = Transaction() context = transaction.context cls.store_cache(requisitions) config = Configuration(1) with transaction.set_context( queue_scheduled_at=config.purchase_process_after, queue_batch=context.get('queue_batch', True)): cls.__queue__.process(requisitions) @classmethod @Workflow.transition('processing') def proceed(cls, requisitions): pass @classmethod @Workflow.transition('done') def do(cls, requisitions): pass @classmethod @ModelView.button def process(cls, requisitions): done = [] process = [] requisitions = [r for r in requisitions if r.state in {'approved', 'processing', 'done'}] cls.create_requests(requisitions) for requisition in requisitions: if requisition.is_done(): if requisition.state != 'done': done.append(requisition) elif requisition.state != 'processing': process.append(requisition) if process: cls.proceed(process) if done: cls.do(done) def is_done(self): return all( r.purchase and r.purchase.state in {'cancelled', 'confirmed'} for l in self.lines for r in l.purchase_requests)