class PartySOM(ModelSQL, ModelView): _name = "ekd.party.som" name = fields.Char('State of Mind', size=64, required=True) factor = fields.Float('Factor', required=True)
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', }, context={ 'company': Eval('company', -1), }, depends={'company'}) party_address = fields.Many2One('party.address', 'Contact Address', domain=[('party', '=', Eval('party'))], states={ 'invisible': Eval('type') != 'project', }) 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'), }), 'get_timesheet_date', setter='set_timesheet_date') timesheet_end_date = fields.Function(fields.Date('Timesheet End', states={ 'invisible': ~Eval('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), # noqa: E711 }), 'get_total') comment = fields.Text('Comment') parent = fields.Many2One( 'project.work', 'Parent', path='path', ondelete='RESTRICT', domain=[ ('company', '=', Eval('company', -1)), ]) path = fields.Char("Path", select=True) children = fields.One2Many('project.work', 'parent', 'Children', domain=[ ('company', '=', Eval('company', -1)), ]) status = fields.Many2One( 'project.work.status', "Status", required=True, select=True, domain=[If(Bool(Eval('type')), ('types', 'in', Eval('type')), ())]) @staticmethod def default_type(): return 'task' @classmethod def default_company(cls): return Transaction().context.get('company') @classmethod def default_status(cls): pool = Pool() WorkStatus = pool.get('project.work.status') return WorkStatus.get_default_status(cls.default_type()) @classmethod def __register__(cls, module_name): TimesheetWork = Pool().get('timesheet.work') transaction = Transaction() cursor = transaction.connection.cursor() update = 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: duration = datetime.timedelta(hours=effort) update.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: update.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: update.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: update.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') 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') # Migration from 5.4: replace state by status table_project_work.not_null_action('state', action='remove') # Migration from 6.0: remove left and right table_project_work.drop_column('left') table_project_work.drop_column('right') @fields.depends('type', 'status') def on_change_type(self): pool = Pool() WorkState = pool.get('project.work.status') if (self.type and (not self.status or self.type not in self.status.types)): self.status = WorkState.get_default_status(self.type) @fields.depends('status', 'progress') def on_change_status(self): if (self.status and self.status.progress is not None and self.status.progress > (self.progress or -1.0)): self.progress = self.status.progress @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_work_progress() def check_work_progress(self): pool = Pool() progress = -1 if self.progress is None else self.progress if (self.status.progress is not None and progress < self.status.progress): Lang = pool.get('ir.lang') lang = Lang.get() raise WorkProgressValidationError( gettext('project.msg_work_invalid_progress_status', work=self.rec_name, progress=lang.format('%.2f%%', self.status.progress * 100), status=self.status.rec_name)) if (self.status.progress == 1.0 and not all(c.progress == 1.0 for c in self.children)): raise WorkProgressValidationError( gettext('project.msg_work_children_progress', work=self.rec_name, status=self.status.rec_name)) if (self.parent and self.parent.progress == 1.0 and not self.progress == 1.0): raise WorkProgressValidationError( gettext('project.msg_work_parent_progress', work=self.rec_name, parent=self.parent.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) 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): pool = Pool() WorkStatus = pool.get('project.work.status') if default is None: default = {} else: default = default.copy() default.setdefault('children', None) default.setdefault('progress', None) default.setdefault( 'status', lambda data: WorkStatus.get_default_status(data['type'])) 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 FloatRequired(ModelSQL): 'Float Required' __name__ = 'test.float_required' float = fields.Float(string='Float', help='Test float', required=True)
class PriceListLine(ModelSQL, ModelView, MatchMixin): 'Price List Line' __name__ = 'product.price_list.line' price_list = fields.Many2One('product.price_list', 'Price List', required=True, ondelete='CASCADE') product = fields.Many2One('product.product', 'Product') sequence = fields.Integer('Sequence') quantity = fields.Float('Quantity', digits=(16, Eval('unit_digits', 2)), depends=['unit_digits']) unit_digits = fields.Function(fields.Integer('Unit Digits'), 'on_change_with_unit_digits') formula = fields.Char( 'Formula', required=True, help=('Python expression that will be evaluated with:\n' '- unit_price: the original unit_price')) @classmethod def __setup__(cls): super(PriceListLine, cls).__setup__() cls._order.insert(0, ('price_list', 'ASC')) cls._order.insert(0, ('sequence', 'ASC')) cls._error_messages.update({ 'invalid_formula': ('Invalid formula "%(formula)s" in price ' 'list line "%(line)s" with exception "%(exception)s".'), }) @classmethod def __register__(cls, module_name): TableHandler = backend.get('TableHandler') cursor = Transaction().cursor table = TableHandler(cursor, cls, module_name) super(PriceListLine, cls).__register__(module_name) # Migration from 2.4: drop required on sequence table.not_null_action('sequence', action='remove') @staticmethod def order_sequence(tables): table, _ = tables[None] return [table.sequence == Null, table.sequence] @staticmethod def default_formula(): return 'unit_price' @fields.depends('product') def on_change_with_unit_digits(self, name=None): if self.product: return self.product.default_uom.digits return 2 @classmethod def validate(cls, lines): super(PriceListLine, cls).validate(lines) for line in lines: line.check_formula() def check_formula(self): ''' Check formula ''' context = self.price_list.get_context_formula(None, None, Decimal('0'), 0, None) try: if not isinstance(self.get_unit_price(**context), Decimal): raise ValueError except Exception, exception: self.raise_user_error( 'invalid_formula', { 'formula': self.formula, 'line': self.rec_name, 'exception': exception, })
class Move: __name__ = 'stock.move' in_anglo_saxon_quantity = fields.Float('Input Anglo-Saxon Quantity', required=True, digits=(16, Eval('unit_digits', 2)), depends=['unit_digits']) out_anglo_saxon_quantity = fields.Float('Output Anglo-Saxon Quantity', required=True, digits=(16, Eval('unit_digits', 2)), depends=['unit_digits']) @classmethod def __setup__(cls): super(Move, cls).__setup__() cls._allow_modify_closed_period.update( ['in_anglo_saxon_quantity', 'out_anglo_saxon_quantity']) cls._sql_constraints += [ ('check_in_anglo_saxon_quantity', 'CHECK(quantity >= in_anglo_saxon_quantity)', 'Anglo-Saxon quantity can not be greater than quantity.'), ('check_out_anglo_saxon_quantity', 'CHECK(quantity >= out_anglo_saxon_quantity)', 'Anglo-Saxon quantity can not be greater than quantity.'), ] @classmethod def __register__(cls, module_name): cursor = Transaction().cursor TableHandler = backend.get('TableHandler') super(Move, cls).__register__(module_name) table = TableHandler(cursor, cls, module_name) # Migration from 2.8: split anglo_saxon_quantity if table.column_exist('anglo_saxon_quantity'): cursor.execute( 'UPDATE "' + cls._table + '" ' 'SET in_anglo_saxon_quantity = anglo_saxon_quantity, ' 'out_anglo_saxon_quantity = anglo_saxon_quantity') table.drop_constraint('check_anglo_saxon_quantity') table.drop_column('anglo_saxon_quantity') @staticmethod def default_in_anglo_saxon_quantity(): return 0.0 @staticmethod def default_out_anglo_saxon_quantity(): return 0.0 def _get_account_stock_move_lines(self, type_): pool = Pool() Uom = pool.get('product.uom') AccountMoveLine = pool.get('account.move.line') Currency = pool.get('currency.currency') lines = super(Move, self)._get_account_stock_move_lines(type_) if (type_.endswith('supplier') and self.product.cost_price_method == 'fixed'): cost_price = Uom.compute_price(self.product.default_uom, self.cost_price, self.uom) with Transaction().set_context(date=self.effective_date): unit_price = Currency.compute(self.currency, self.unit_price, self.company.currency, round=False) amount = self.company.currency.round( Decimal(str(self.quantity)) * (unit_price - cost_price)) if self.company.currency.is_zero(amount): return lines account = self.product.account_stock_supplier_used for move_line in lines: if move_line.account == account: break else: return lines if type_.startswith('in_'): move_line.credit += amount debit = amount credit = Decimal(0) else: move_line.debit += amount debit = Decimal(0) credit = amount if amount < Decimal(0): debit, credit = -credit, -debit move_line = AccountMoveLine( debit=debit, credit=credit, account=self.product.account_expense_used, ) lines.append(move_line) return lines @classmethod def _get_anglo_saxon_move(cls, moves, quantity, type_): ''' Generator of (move, qty, cost_price) where move is the move to be consumed, qty is the quantity (in the product default uom) to be consumed on this move and cost_price is in the company currency. ''' pool = Pool() Uom = pool.get('product.uom') Currency = pool.get('currency.currency') as_qty_field = _get_field(type_) consumed_qty = 0.0 for move in moves: qty = Uom.compute_qty(move.uom, move.quantity - getattr(move, as_qty_field), move.product.default_uom, round=False) if qty <= 0.0: continue if qty > quantity - consumed_qty: qty = quantity - consumed_qty if consumed_qty >= quantity: break if type_ == 'in_supplier': with Transaction().set_context(date=move.effective_date): unit_price = Currency.compute(move.currency, move.unit_price, move.company.currency, round=False) cost_price = Uom.compute_price(move.uom, unit_price, move.product.default_uom) else: cost_price = move.cost_price yield (move, qty, cost_price) consumed_qty += qty @classmethod def update_anglo_saxon_quantity_product_cost(cls, product, moves, quantity, uom, type_): ''' Return the cost for quantity based on lines. Update anglo_saxon_quantity on the concerned moves. ''' pool = Pool() Uom = pool.get('product.uom') for move in moves: assert move.product == product, 'wrong product' assert type_.startswith('in_') or type_.startswith('out_'), \ 'wrong type' total_qty = Uom.compute_qty(uom, quantity, product.default_uom, round=False) as_qty_field = _get_field(type_) cost = Decimal('0.0') consumed_qty = 0.0 for move, move_qty, move_cost_price in cls._get_anglo_saxon_move( moves, total_qty, type_): consumed_qty += move_qty cost += move_cost_price * Decimal(str(move_qty)) move_qty = Uom.compute_qty(product.default_uom, move_qty, move.uom) cls.write([move], { as_qty_field: ((getattr(move, as_qty_field) or 0.0) + move_qty), }) if consumed_qty < total_qty: qty = total_qty - consumed_qty consumed_qty += qty cost += product.cost_price * Decimal(str(qty)) return cost @classmethod def copy(cls, moves, default=None): if default is None: default = {} default = default.copy() for prefix in ('in_', 'out_'): default.setdefault( prefix + 'anglo_saxon_quantity', getattr(cls, 'default_%sanglo_saxon_quantity' % prefix)()) return super(Move, cls).copy(moves, default=default)
class OrderLineComponentMixin(ModelStorage): line = fields.Many2One(prefix + '.line', "Line", required=True, ondelete='CASCADE', domain=[ ('product.type', '=', 'kit'), ]) moves = fields.One2Many('stock.move', 'origin', 'Moves', readonly=True) moves_ignored = fields.Many2Many(prefix + '.line.component-ignored-stock.move', 'component', 'move', "Ignored Moves", readonly=True) moves_recreated = fields.Many2Many( prefix + '.line.component-recreated-stock.move', 'component', 'move', "Recreated Moves", readonly=True) move_done = fields.Function(fields.Boolean('Moves Done'), 'get_move_done') move_exception = fields.Function(fields.Boolean('Moves Exception'), 'get_move_exception') quantity_ratio = fields.Float("Quantity Ratio", readonly=True, states={ 'required': ~Eval('fixed', False), }) price_ratio = fields.Numeric("Price Ratio", readonly=True, required=True) @classmethod def __setup__(cls): super().__setup__() cls.__access__.add('line') @fields.depends('line', '_parent_line.product') def on_change_with_parent_type(self, name=None): if self.line and self.line.product: return self.line.product.type @classmethod def set_price_ratio(cls, components): "Set price ratio between components" pool = Pool() Uom = pool.get('product.uom') list_prices = defaultdict(Decimal) sum_ = 0 for component in components: product = component.product list_price = Uom.compute_price( product.default_uom, product.list_price, component.unit) * Decimal(str(component.quantity)) list_prices[component] = list_price sum_ += list_price for component in components: if sum_: ratio = list_prices[component] / sum_ else: ratio = 1 / len(components) component.price_ratio = ratio def get_move(self, type_): raise NotImplementedError def _get_shipped_quantity(self, shipment_type): 'Return the quantity already shipped' pool = Pool() Uom = pool.get('product.uom') quantity = 0 skips = set(self.moves_recreated) for move in self.moves: if move not in skips: quantity += Uom.compute_qty(move.uom, move.quantity, self.unit) return quantity @property def _move_remaining_quantity(self): "Compute the remaining quantity to ship" pool = Pool() Uom = pool.get('product.uom') ignored = set(self.moves_ignored) quantity = abs(self.quantity) for move in self.moves: if move.state == 'done' or move in ignored: quantity -= Uom.compute_qty(move.uom, move.quantity, self.unit) return quantity def get_move_done(self, name): quantity = self._move_remaining_quantity if quantity is None: return True else: return self.unit.round(quantity) <= 0 def get_move_exception(self, name): skips = set(self.moves_ignored) skips.update(self.moves_recreated) for move in self.moves: if move.state == 'cancelled' and move not in skips: return True return False def get_moved_ratio(self): pool = Pool() Uom = pool.get('product.uom') quantity = 0 for move in self.moves: if move.state != 'done': continue qty = Uom.compute_qty(move.uom, move.quantity, self.unit) dest_type = self.line.to_location.type if (move.to_location.type == dest_type and move.from_location.type != dest_type): quantity += qty elif (move.from_location.type == dest_type and move.to_location.type != dest_type): quantity -= qty if self.quantity < 0: quantity *= -1 return quantity / self.quantity def get_rec_name(self, name): return super().get_rec_name(name) + (' @ %s' % self.line.rec_name) @classmethod def search_rec_name(cls, name, clause): return super().search_rec_name(name, clause) + [ ('line.rec_name', ) + tuple(clause[1:]), ]
class InventoryLine(ModelSQL, ModelView): 'Stock Inventory Line' __name__ = 'stock.inventory.line' _states = { 'readonly': Eval('inventory_state') != 'draft', } _depends = ['inventory_state'] product = fields.Many2One('product.product', 'Product', required=True, domain=[ ('type', '=', 'goods'), ], states=_states, depends=_depends) uom = fields.Function( fields.Many2One('product.uom', 'UOM', help="The unit in which the quantity is specified."), 'get_uom') unit_digits = fields.Function(fields.Integer('Unit Digits'), 'get_unit_digits') expected_quantity = fields.Float( 'Expected Quantity', required=True, digits=(16, Eval('unit_digits', 2)), readonly=True, states={ 'invisible': Eval('id', -1) < 0, }, depends=['unit_digits'], help="The quantity the system calculated should be in the location.") quantity = fields.Float('Quantity', digits=(16, Eval('unit_digits', 2)), states=_states, depends=['unit_digits'] + _depends, help="The actual quantity found in the location.") moves = fields.One2Many('stock.move', 'origin', 'Moves', readonly=True) inventory = fields.Many2One('stock.inventory', 'Inventory', required=True, ondelete='CASCADE', states={ 'readonly': _states['readonly'] & Bool(Eval('inventory')), }, depends=_depends, help="The inventory the line belongs to.") inventory_location = fields.Function(fields.Many2One( 'stock.location', "Location"), 'on_change_with_inventory_location', searcher='search_inventory_location') inventory_date = fields.Function(fields.Date("Date"), 'on_change_with_inventory_date', searcher='search_inventory_date') inventory_state = fields.Function( fields.Selection('get_inventory_states', "Inventory State"), 'on_change_with_inventory_state') @classmethod def __setup__(cls): super(InventoryLine, cls).__setup__() t = cls.__table__() cls._sql_constraints += [ ('check_line_qty_pos', Check(t, t.quantity >= 0), 'stock.msg_inventory_line_quantity_positive'), ] cls._order.insert(0, ('product', 'ASC')) @classmethod def __register__(cls, module_name): cursor = Transaction().connection.cursor() pool = Pool() Move = pool.get('stock.move') sql_table = cls.__table__() move_table = Move.__table__() super(InventoryLine, cls).__register__(module_name) table = cls.__table_handler__(module_name) # Migration from 3.0: use Move origin if table.column_exist('move'): cursor.execute(*sql_table.select( sql_table.id, sql_table.move, where=sql_table.move != Null)) for line_id, move_id in cursor.fetchall(): cursor.execute(*move_table.update( columns=[move_table.origin], values=['%s,%s' % (cls.__name__, line_id)], where=move_table.id == move_id)) table.drop_column('move') # Migration from 4.6: drop required on quantity table.not_null_action('quantity', action='remove') @staticmethod def default_unit_digits(): return 2 @staticmethod def default_expected_quantity(): return 0. @fields.depends('product') def on_change_product(self): self.unit_digits = 2 if self.product: self.uom = self.product.default_uom self.unit_digits = self.product.default_uom.digits @fields.depends('inventory', '_parent_inventory.location') def on_change_with_inventory_location(self, name=None): if self.inventory and self.inventory.location: return self.inventory.location.id @classmethod def search_inventory_location(cls, name, clause): nested = clause[0].lstrip(name) return [('inventory.' + name + nested, ) + tuple(clause[1:])] @fields.depends('inventory', '_parent_inventory.date') def on_change_with_inventory_date(self, name=None): if self.inventory: return self.inventory.date @classmethod def search_inventory_date(cls, name, clause): return [('inventory.date', ) + tuple(clause[1:])] @classmethod def get_inventory_states(cls): pool = Pool() Inventory = pool.get('stock.inventory') return Inventory.fields_get(['state'])['state']['selection'] @fields.depends('inventory', '_parent_inventory.state') def on_change_with_inventory_state(self, name=None): if self.inventory: return self.inventory.state return 'draft' def get_rec_name(self, name): return self.product.rec_name @classmethod def search_rec_name(cls, name, clause): return [('product.rec_name', ) + tuple(clause[1:])] def get_uom(self, name): return self.product.default_uom.id def get_unit_digits(self, name): return self.product.default_uom.digits @property def unique_key(self): key = [] for fname in self.inventory.grouping(): value = getattr(self, fname) if isinstance(value, Model): value = value.id key.append(value) return tuple(key) @classmethod def cancel_move(cls, lines): Move = Pool().get('stock.move') moves = [m for l in lines for m in l.moves if l.moves] Move.cancel(moves) Move.delete(moves) def get_move(self): ''' Return Move instance for the inventory line ''' pool = Pool() Move = pool.get('stock.move') Uom = pool.get('product.uom') qty = self.quantity if qty is None: if self.inventory.empty_quantity is None: raise InventoryValidationError( gettext('stock.msg_inventory_missing_empty_quantity', inventory=self.inventory.rec_name)) if self.inventory.empty_quantity == 'keep': return else: qty = 0.0 delta_qty = Uom.compute_qty(self.uom, self.expected_quantity - qty, self.uom) if delta_qty == 0.0: return from_location = self.inventory.location to_location = self.inventory.location.lost_found_used if not to_location: raise InventoryValidationError( gettext('stock.msg_inventory_location_missing_lost_found', inventory=self.inventory.rec_name, location=self.inventory.location.rec_name)) if delta_qty < 0: (from_location, to_location, delta_qty) = \ (to_location, from_location, -delta_qty) return Move( from_location=from_location, to_location=to_location, quantity=delta_qty, product=self.product, uom=self.uom, company=self.inventory.company, effective_date=self.inventory.date, origin=self, ) def update_values4complete(self, quantity): ''' Return update values to complete inventory ''' values = {} # if nothing changed, no update if self.expected_quantity == quantity: return values values['expected_quantity'] = quantity return values @classmethod def create_values4complete(cls, inventory, quantity): ''' Return create values to complete inventory ''' return { 'inventory': inventory.id, 'expected_quantity': quantity, } @classmethod def delete(cls, lines): for line in lines: if line.inventory_state not in {'cancelled', 'draft'}: raise AccessError( gettext('stock.msg_inventory_line_delete_cancel', line=line.rec_name, inventory=line.inventory.rec_name)) super(InventoryLine, cls).delete(lines)
class Line(metaclass=PoolMeta): __name__ = 'sale.line' secondary_quantity = fields.Function(fields.Float( "Secondary Quantity", digits=(16, Eval('secondary_unit_digits', 2)), states={ 'invisible': ((Eval('type') != 'line') | ~Eval('secondary_unit')), 'readonly': Eval('sale_state') != 'draft', }, depends=[ 'secondary_unit_digits', 'type', 'secondary_unit', 'sale_state' ]), 'on_change_with_secondary_quantity', setter='set_secondary') secondary_unit = fields.Many2One( 'product.uom', "Secondary Unit", ondelete='RESTRICT', domain=[ If( Eval('sale_state') == 'draft', ('category', '=', Eval('product_secondary_uom_category')), ()), ], states={ 'invisible': ((Eval('type') != 'line') | (~Eval('secondary_uom_factor') & ~Eval('secondary_uom_rate'))), 'readonly': Eval('sale_state') != 'draft', }, depends=[ 'product_secondary_uom_category', 'type', 'secondary_uom_factor', 'secondary_uom_rate', 'sale_state' ]) secondary_unit_price = fields.Function( fields.Numeric("Secondary Unit Price", digits=price_digits, states={ 'invisible': ((Eval('type') != 'line') | ~Eval('secondary_unit')), 'readonly': Eval('sale_state') != 'draft', }, depends=['type', 'secondary_unit', 'sale_state']), 'on_change_with_secondary_unit_price', setter='set_secondary') secondary_uom_factor = fields.Float("Secondary UOM Factor") secondary_uom_rate = fields.Float("Secondary UOM Rate") secondary_unit_digits = fields.Function( fields.Integer("Secondary Unit Digits"), 'on_change_with_secondary_unit_digits') product_secondary_uom_category = fields.Function( fields.Many2One('product.uom.category', "Product Secondary UOM Category"), 'on_change_with_product_secondary_uom_category') @fields.depends('product') def _secondary_record(self): if self.product and self.product.sale_secondary_uom: return self.product @fields.depends('quantity', 'unit', 'secondary_unit', 'secondary_uom_factor', 'secondary_uom_rate') def on_change_with_secondary_quantity(self, name=None): pool = Pool() Uom = pool.get('product.uom') if (self.quantity and self.unit and self.secondary_unit and (self.secondary_uom_factor or self.secondary_uom_rate)): return Uom.compute_qty(self.unit, self.quantity, self.secondary_unit, round=True, factor=self.secondary_uom_factor, rate=self.secondary_uom_rate) else: return None @fields.depends('secondary_quantity', 'secondary_unit', 'unit', 'secondary_uom_factor', 'secondary_uom_rate', methods=['on_change_quantity', 'on_change_with_amount']) def on_change_secondary_quantity(self): pool = Pool() Uom = pool.get('product.uom') if (self.secondary_quantity and self.secondary_unit and self.unit and (self.secondary_uom_factor or self.secondary_uom_rate)): self.quantity = Uom.compute_qty(self.secondary_unit, self.secondary_quantity, self.unit, round=True, factor=self.secondary_uom_rate, rate=self.secondary_uom_factor) self.on_change_quantity() self.amount = self.on_change_with_amount() @fields.depends('unit_price', 'unit', 'secondary_unit', 'secondary_uom_factor', 'secondary_uom_rate') def on_change_with_secondary_unit_price(self, name=None): pool = Pool() Uom = pool.get('product.uom') if (self.unit_price is not None and self.unit and self.secondary_unit and (self.secondary_uom_factor or self.secondary_uom_rate)): unit_price = Uom.compute_price(self.unit, self.unit_price, self.secondary_unit, factor=self.secondary_uom_factor, rate=self.secondary_uom_rate) return round_price(unit_price) else: return None @fields.depends('secondary_unit_price', 'secondary_unit', 'unit', 'secondary_uom_factor', 'secondary_uom_rate', methods=['on_change_with_amount']) def on_change_secondary_unit_price(self, name=None): pool = Pool() Uom = pool.get('product.uom') if (self.secondary_unit_price is not None and self.secondary_unit and self.unit and (self.secondary_uom_factor or self.secondary_uom_rate)): self.unit_price = Uom.compute_price(self.secondary_unit, self.secondary_unit_price, self.unit, factor=self.secondary_uom_rate, rate=self.secondary_uom_factor) self.unit_price = round_price(self.unit_price) self.amount = self.on_change_with_amount() @fields.depends(methods=[ 'on_change_secondary_quantity', 'on_change_secondary_unit_price' ]) def on_change_secondary_unit(self): self.on_change_secondary_quantity() self.on_change_secondary_unit_price() @fields.depends('secondary_unit') def on_change_with_secondary_unit_digits(self, name=None): if self.secondary_unit: return self.secondary_unit.digits @fields.depends(methods=['_secondary_record']) def on_change_with_product_secondary_uom_category(self, name=None): secondary_record = self._secondary_record() if secondary_record: return secondary_record.sale_secondary_uom.category.id @classmethod def set_secondary(cls, lines, name, value): pass @fields.depends( 'secondary_unit', methods=['on_change_with_secondary_quantity', '_secondary_record']) def on_change_product(self): super().on_change_product() secondary_record = self._secondary_record() if secondary_record and self.secondary_unit: secondary_uom = secondary_record.sale_secondary_uom if secondary_uom: if self.secondary_unit.category != secondary_uom.category: self.secondary_unit = None if secondary_record: self.secondary_uom_factor = ( secondary_record.sale_secondary_uom_normal_factor) self.secondary_uom_rate = ( secondary_record.sale_secondary_uom_normal_rate) else: self.secondary_unit = None self.secondary_uom_factor = None self.secondary_uom_rate = None self.secondary_quantity = self.on_change_with_secondary_quantity() def get_invoice_line(self): pool = Pool() InvoiceLine = pool.get('account.invoice.line') lines = super().get_invoice_line() if hasattr(InvoiceLine, 'secondary_unit'): for line in lines: if line.type != 'line': continue if line.unit == self.unit: line.secondary_unit = self.secondary_unit return lines def get_move(self, shipment_type): move = super().get_move(shipment_type) if move and hasattr(move.__class__, 'secondary_unit'): if move.uom == self.unit: move.secondary_unit = self.secondary_unit return move
class BudgetLine(ModelSQL, ModelView): 'General Budget Lines' _name = 'ekd.account.budget.line' _description = __doc__ __sequence_line = 0 budget = fields.Many2One('ekd.account.budget', 'Budget', ondelete="RESTRICT") direct_line = fields.Selection([('income', 'Income'), ('expense', 'expense'), ('total_sum', 'Total Summa'), ('total_per', 'Total Percentage')], 'Direct Budget', required=True) income = fields.Boolean('Income Budget') kind = fields.Selection([('quantity', 'Quantity'), ('amount', 'Amount'), ('quan_amount', 'Quantity and Amount'), ('percentage', 'Percentage')], 'Kind') type_line = fields.Selection([('section', 'Section'), ('subtotal', 'Subtotal'), ('line', 'Line')], 'Type line') analytic = fields.Many2One('ekd.account.analytic', 'Analytic Account', domain=[('kind_analytic', '=', Eval('direct_line'))], ondelete="RESTRICT") code_section = fields.Char('Budget Section', size=None, help="Code Section") code = fields.Char('Budget Item', size=None, help="Code Item") name = fields.Char('Name', size=None, required=True, on_change_with=['analytic', 'name']) sequence = fields.Char('Code Line', size=None, required=True) parent = fields.Many2One('ekd.account.budget.line', 'Parent', ondelete="RESTRICT") # , domain=[('budget', '=', budget.id)]) childs = fields.One2Many('ekd.account.budget.line', 'parent', 'Children') product = fields.Many2One('product.product', 'Product') quantity = fields.Float('Quantity', digits=(16, Eval('unit_digits', 2)), on_change=['quantity', 'price_unit'], states={ 'invisible': Or(Equal(Eval('kind'), 'amount'), Not(Equal(Eval('type_line'), 'line'))), 'tree_invisible': Or(Equal(Eval('kind'), 'amount'), Not(Equal(Eval('type_line'), 'line'))) }, depends=['kind', 'type_line']) uom = fields.Many2One('product.uom', 'Unit', states={ 'invisible': Or(Equal(Eval('kind'), 'amount'), Not(Equal(Eval('type_line'), 'line'))), 'tree_invisible': Or(Equal(Eval('kind'), 'amount'), Not(Equal(Eval('type_line'), 'line'))) }, depends=['kind', 'type_line']) unit_digits = fields.Function( fields.Integer('Unit Digits', on_change_with=['uom']), 'get_unit_digits') price_unit = fields.Numeric('Price unit', digits=(16, 2), states={ 'invisible': Or(Equal(Eval('kind'), 'amount'), Not(Equal(Eval('type_line'), 'line'))), 'tree_invisible': Or(Equal(Eval('kind'), 'amount'), Equal(Eval('type_line'), 'line')) }, depends=['kind', 'type_line'], on_change=['quantity', 'price_unit']) amount = fields.Numeric( 'Amount', digits=(16, 2), states={'readonly': Not(Equal(Eval('state'), 'draft'))}) amount_change = fields.Function( fields.Numeric('Amount Change', digits=(16, 2), states={ 'invisible': Equal(Eval('state'), 'draft'), 'tree_invisible': Equal(Eval('state'), 'draft') }), 'get_amount_change') amount_move = fields.Function( fields.Numeric('Amount Accounting', digits=(16, 2), states={ 'invisible': Equal(Eval('state'), 'draft'), 'tree_invisible': Equal(Eval('state'), 'draft') }), 'get_amount_move') quantity_move = fields.Function( fields.Numeric('Quantity Accounting', digits=(16, 2), states={ 'invisible': Equal(Eval('state'), 'draft'), 'tree_invisible': Equal(Eval('state'), 'draft') }), 'get_amount_move') currency = fields.Many2One( 'currency.currency', 'Currency', states={'invisible': Equal(Eval('kind'), 'quantity')}, depends=['kind']) line_change = fields.One2Many('ekd.account.budget.line.change', 'budget_line', 'Budget Line Change', states={ 'invisible': Equal(Eval('state'), 'draft'), 'readonly': Equal(Eval('state'), 'draft'), }, context={ 'budget': Eval('budget'), 'name': Eval('name'), 'uom': Eval('uom'), 'analityc': Eval('analytic'), 'quantity': Eval('quantity'), 'unit_digits': Eval('unit_digits'), 'price_unit': Eval('price_unit'), 'amount': Eval('amount'), }) move_line = fields.One2Many('ekd.account.move.line.budget', 'budget_line', 'Amount Accounting', states={ 'invisible': Equal(Eval('state'), 'draft'), 'readonly': Equal(Eval('state'), 'draft'), }) state = fields.Selection([('draft', 'Draft'), ('running', 'Performed'), ('done', 'Completed'), ('deleted', 'Deleted')], 'State', required=True, readonly=True) def __init__(self): super(BudgetLine, self).__init__() self._sql_constraints += [ ('code_uniq', 'UNIQUE(budget,sequence)', 'The sequence in the budget must be unique!'), ] self._order.insert(0, ('budget', 'ASC')) self._order.insert(0, ('sequence', 'ASC')) self._order.insert(1, ('analytic', 'ASC')) self._rpc.update({ 'on_write': True, }) def default_type_line(self): return 'line' def default_state(self): return 'draft' def default_sequence(self): self.__sequence_line = self.__sequence_line + 10 if self.__sequence_line < 100: return "00%s" % (self.__sequence_line) elif self.__sequence_line < 1000: return "0%s" % (self.__sequence_line) else: return str(self.__sequence_line) def default_kind(self): return 'quan_amount' def default_direct_line(self): return 'expense' def default_type_line(self): return 'line' def default_income(self): return False def get_unit_digits(self, ids, name): res = {} for line in self.browse(ids): if line.uom: res[line.id] = line.uom.digits else: res[line.id] = 2 return res def get_amount_change(self, ids, name): res = {}.fromkeys(ids, Decimal('0.0')) for line in self.browse(ids): if line.line_change: for line_change in line.line_change: res[line.id] += line_change.amount res[line.id] += line.amount return res def get_amount_move(self, ids, names): res = {} for name in names: res.setdefault(name, {}) for line in self.browse(ids): if name == 'amount_move': res[name][line.id] = Decimal('0.0') if name == 'quantity_move': res[name][line.id] = 0.0 if line.move_line: for move_line in line.move_line: res[name][line.id] += move_line.amount return res def on_write(self, ids): lines = self.browse(ids) res = [] for line in lines: res.extend([x.id for x in line.move.lines]) # raise Exception(str(res), ids, str(context)) return list({}.fromkeys(res)) def on_change_with_name(self, vals): if not vals.get('analytic'): return analytic_obj = self.pool.get('ekd.account.analytic') if not vals.get('name'): return analytic_obj.browse(vals.get('analytic')).rec_name def on_change_with_unit_digits(self, vals): if not vals.get('uom'): return 2 uom_obj = self.pool.get('product.uom') return uom_obj.browse(vals.get('uom')).digits def on_change_with_amount(self, vals): if not vals.get('quantity') or not vals.get('price_unit'): return vals return Decimal(str(vals.get('quantity'))) * Decimal( str(vals.get('price_unit'))) def on_change_quantity(self, vals): if not vals.get('quantity') or not vals.get('price_unit'): return vals return { 'amount': Decimal(str(vals.get('quantity'))) * Decimal(str(vals.get('price_unit'))) } def on_change_price_unit(self, vals): if not vals.get('quantity') or not vals.get('price_unit'): return vals return { 'amount': Decimal(str(vals.get('quantity'))) * Decimal(str(vals.get('price_unit'))) } def delete(self, ids): return super(BudgetLine, self).delete(ids) def create(self, vals): return super(BudgetLine, self).create(vals) def write(self, ids, vals): return super(BudgetLine, self).write(ids, vals)
class AmendmentLine(ModelSQL, ModelView): "Sale Amendment Line" __name__ = 'sale.amendment.line' amendment = fields.Many2One('sale.amendment', "Amendment", required=True) state = fields.Function(fields.Selection('get_states', "State"), 'on_change_with_state') action = fields.Selection([ ('taxes', "Recompute Taxes"), ('payment_term', "Change Payment Term"), ('party', "Change Parties and Addresses"), ('warehouse', "Change Warehouse"), ('line', "Change Line"), ], "Action", required=True, states={ 'readonly': Eval('state') != 'draft', }, depends=['state']) sale = fields.Function(fields.Many2One('sale.sale', "Sale"), 'on_change_with_sale') line = fields.Many2One('sale.line', "Line", domain=[ ('type', '=', 'line'), ('sale', '=', Eval('sale', -1)), ], states={ 'readonly': Eval('state') != 'draft', 'invisible': Eval('action') != 'line', 'required': Eval('action') == 'line', }, depends=['state', 'sale', 'action']) payment_term = fields.Many2One('account.invoice.payment_term', "Payment Term", states={ 'invisible': Eval('action') != 'payment_term', }, depends=['action']) party = fields.Many2One('party.party', "Party", states={ 'readonly': Eval('state') != 'draft', 'invisible': Eval('action') != 'party', 'required': Eval('action') == 'party', }, depends=['state', 'action']) invoice_address = fields.Many2One('party.address', "Invoice Address", domain=[ ('party', '=', Eval('party')), ], states={ 'readonly': Eval('state') != 'draft', 'invisible': Eval('action') != 'party', 'required': Eval('action') == 'party', }, depends=['state', 'action', 'party']) shipment_party = fields.Many2One('party.address', "Shipment Party", states={ 'readonly': Eval('state') != 'draft', 'invisible': Eval('action') != 'party', }, depends=['state', 'action']) shipment_address = fields.Many2One( 'party.address', "Shipment Address", domain=[ ('party', '=', If(Eval('shipment_party'), Eval('shipment_party'), Eval('party'))), ], states={ 'readonly': Eval('state') != 'draft', 'invisible': Eval('action') != 'party', 'required': Eval('action') == 'party', }, depends=['state', 'action', 'party', 'shipment_party']) warehouse = fields.Many2One('stock.location', "Warehouse", domain=[ ('type', '=', 'warehouse'), ], states={ 'readonly': Eval('state') != 'draft', 'invisible': Eval('action') != 'warehouse', 'required': Eval('action') == 'warehouse', }, depends=['state', 'action']) product = fields.Many2One( 'product.product', "Product", domain=[ If(Eval('state') == 'draft', ('salable', '=', True), ()), ('default_uom_category', '=', Eval('product_uom_category')), ], states={ 'readonly': Eval('state') != 'draft', 'invisible': Eval('action') != 'line', 'required': Eval('action') == 'line', }, depends=['action', 'state', 'product_uom_category']) quantity = fields.Float("Quantity", digits=(16, Eval('unit_digits', 2)), states={ 'readonly': Eval('state') != 'draft', 'invisible': Eval('action') != 'line', 'required': Eval('action') == 'line', }, depends=['unit_digits', 'action']) unit = fields.Many2One('product.uom', "Unit", ondelete='RESTRICT', domain=[ ('category', '=', Eval('product_uom_category')), ], states={ 'readonly': Eval('state') != 'draft', 'invisible': Eval('action') != 'line', 'required': Eval('action') == 'line', }, depends=['state', 'product_uom_category', 'action']) unit_price = fields.Numeric("Unit Price", digits=price_digits, states={ 'readonly': Eval('state') != 'draft', 'invisible': Eval('action') != 'line', 'required': Eval('action') == 'line', }, depends=['state', 'action']) description = fields.Text("Description", states={ 'readonly': Eval('state') != 'draft', 'invisible': Eval('action') != 'line', }, depends=['state', 'action']) 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') @fields.depends('amendment', '_parent_amendment.sale', '_parent_amendment._parent_sale.payment_term', '_parent_amendment._parent_sale.party', '_parent_amendment._parent_sale.invoice_address', '_parent_amendment._parent_sale.shipment_party', '_parent_amendment._parent_sale.shipment_address', '_parent_amendment._parent_sale.warehouse') def on_change_amendment(self): if self.amendment and self.amendment.sale: self.payment_term = self.amendment.sale.payment_term self.party = self.amendment.sale.party self.invoice_address = self.amendment.sale.invoice_address self.shipment_party = self.amendment.sale.shipment_party self.shipment_address = self.amendment.sale.shipment_address self.warehouse = self.amendment.sale.warehouse @fields.depends(methods=['on_change_amendment']) def on_change_state(self): self.on_change_amendment() @classmethod def get_states(cls): pool = Pool() Amendment = pool.get('sale.amendment') return Amendment.fields_get(['state'])['state']['selection'] @fields.depends('amendment', '_parent_amendment.state') def on_change_with_state(self, name=None): if self.amendment: return self.amendment.state @fields.depends(methods=['on_change_line', 'on_change_amendment']) def on_change_action(self): self.line = None self.on_change_line() self.on_change_amendment() @fields.depends('amendment', '_parent_amendment.sale') def on_change_with_sale(self, name=None): if self.amendment and self.amendment.sale: return self.amendment.sale.id @fields.depends('line') def on_change_line(self): if self.line: self.product = self.line.product self.quantity = self.line.quantity self.unit = self.line.unit self.unit_price = self.line.unit_price else: self.product = self.quantity = self.unit = self.unit_price = None @fields.depends('party', 'shipment_party') def on_change_party(self): self.invoice_address = None if not self.shipment_party: self.shipment_address = None if self.party: self.invoice_address = self.party.address_get(type='invoice') if not self.shipment_party: self.shipment_address = self.party.address_get(type='delivery') @fields.depends('party', 'shipment_party') def on_change_shipment_party(self): if self.shipment_party: self.shipment_address = self.shipment_party.address_get( type='delivery') elif self.party: self.shipment_address = self.party.address_get(type='delivery') @fields.depends('unit') def on_change_with_unit_digits(self, name=None): if self.unit: return self.unit.digits @fields.depends('line') def on_change_with_product_uom_category(self, name=None): if self.line: return self.line.product_uom_category.id def apply(self, sale): assert self.sale == sale sale_line = None if self.line: for line in sale.lines: if self.line == line: sale_line = line break getattr(self, '_apply_%s' % self.action)(sale, sale_line) def _apply_taxes(self, sale, sale_line): for line in sale.lines: line.taxes = line.compute_taxes(sale.party) def _apply_payment_term(self, sale, sale_line): sale.payment_term = self.payment_term def _apply_party(self, sale, sale_line): sale.party = self.party sale.invoice_address = self.invoice_address sale.shipment_party = self.shipment_party sale.shipment_address = self.shipment_address def _apply_warehouse(self, sale, sale_line): sale.warehouse = self.warehouse def _apply_line(self, sale, sale_line): sale_line.product = self.product sale_line.quantity = self.quantity sale_line.unit = self.unit sale_line.unit_price = self.unit_price sale_line.description = self.description
class Asset(Workflow, ModelSQL, ModelView): 'Asset' __name__ = 'account.asset' _rec_name = 'number' number = fields.Char('Number', readonly=True, select=True) product = fields.Many2One('product.product', 'Product', required=True, states={ 'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')), }, depends=['state'], domain=[ ('type', '=', 'assets'), ('depreciable', '=', True), ]) supplier_invoice_line = fields.Many2One( 'account.invoice.line', 'Supplier Invoice Line', domain=[ If( Eval('product', None) == None, ('product', '=', -1), ('product', '=', Eval('product', -1)), ), ('invoice.type', '=', 'in'), [ 'OR', ('company', '=', Eval('company', -1)), ('invoice.company', '=', Eval('company', -1)), ], ], states={ 'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')), }, depends=['product', 'state', 'company']) customer_invoice_line = fields.Function( fields.Many2One('account.invoice.line', 'Customer Invoice Line'), 'get_customer_invoice_line') account_journal = fields.Many2One('account.journal', 'Journal', states={ 'readonly': Eval('state') != 'draft', }, depends=['state'], domain=[('type', '=', 'asset')], required=True) company = fields.Many2One('company.company', 'Company', states={ 'readonly': Eval('state') != 'draft', }, depends=['state'], required=True) currency_digits = fields.Function(fields.Integer('Currency Digits'), 'on_change_with_currency_digits') quantity = fields.Float('Quantity', digits=(16, Eval('unit_digits', 2)), states={ 'readonly': (Bool(Eval('supplier_invoice_line', 1)) | Eval('lines', [0]) | (Eval('state') != 'draft')), }, depends=['state', 'unit_digits']) unit = fields.Many2One('product.uom', 'Unit', states={ 'readonly': (Bool(Eval('product')) | (Eval('state') != 'draft')), }, depends=['state']) unit_digits = fields.Function(fields.Integer('Unit Digits'), 'on_change_with_unit_digits') value = fields.Numeric('Value', digits=(16, Eval('currency_digits', 2)), states={ 'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')), }, depends=['currency_digits', 'state'], required=True) residual_value = fields.Numeric( 'Residual Value', states={ 'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')), }, depends=['currency_digits', 'state'], required=True, digits=(16, Eval('currency_digits', 2))) purchase_date = fields.Date('Purchase Date', states={ 'readonly': (Bool(Eval('supplier_invoice_line', 1)) | Eval('lines', [0]) | (Eval('state') != 'draft')), }, required=True, depends=['state']) start_date = fields.Date( 'Start Date', states={ 'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')), }, required=True, domain=[('start_date', '<=', Eval('end_date', None))], depends=['state', 'end_date']) end_date = fields.Date( 'End Date', states={ 'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')), }, required=True, domain=[('end_date', '>=', Eval('start_date', None))], depends=['state', 'start_date']) depreciation_method = fields.Selection( [ ('linear', 'Linear'), ], 'Depreciation Method', states={ 'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')), }, required=True, depends=['state']) frequency = fields.Selection([ ('monthly', 'Monthly'), ('yearly', 'Yearly'), ], 'Frequency', required=True, states={ 'readonly': (Eval('lines', [0]) | (Eval('state') != 'draft')), }, depends=['state']) state = fields.Selection([ ('draft', 'Draft'), ('running', 'Running'), ('closed', 'Closed'), ], 'State', readonly=True) lines = fields.One2Many('account.asset.line', 'asset', 'Lines', readonly=True) move = fields.Many2One('account.move', 'Account Move', readonly=True, domain=[ ('company', '=', Eval('company', -1)), ], depends=['company']) update_moves = fields.Many2Many('account.asset-update-account.move', 'asset', 'move', 'Update Moves', readonly=True, domain=[ ('company', '=', Eval('company', -1)), ], states={ 'invisible': ~Eval('update_moves'), }, depends=['company']) comment = fields.Text('Comment') @classmethod def __setup__(cls): super(Asset, cls).__setup__() table = cls.__table__() cls._sql_constraints = [ ('invoice_line_uniq', Unique(table, table.supplier_invoice_line), 'Supplier Invoice Line can be used only once on asset.'), ] cls._error_messages.update({ 'delete_draft': 'Asset "%s" must be in draft to be deleted.', }) cls._transitions |= set(( ('draft', 'running'), ('running', 'closed'), )) cls._buttons.update({ 'run': { 'invisible': Eval('state') != 'draft', }, 'close': { 'invisible': Eval('state') != 'running', }, 'create_lines': { 'invisible': (Eval('lines', []) | (Eval('state') != 'draft')), }, 'clear_lines': { 'invisible': (~Eval('lines', [0]) | (Eval('state') != 'draft')), }, 'update': { 'invisible': Eval('state') != 'running', }, }) @classmethod def __register__(cls, module_name): TableHandler = backend.get('TableHandler') table_h = TableHandler(cls, module_name) # Migration from 3.8: rename reference into number if table_h.column_exist('reference'): table_h.column_rename('reference', 'number') super(Asset, cls).__register__(module_name) @staticmethod def default_state(): return 'draft' @staticmethod def default_frequency(): return 'monthly' @staticmethod def default_depreciation_method(): return 'linear' @staticmethod def default_start_date(): return Pool().get('ir.date').today() @staticmethod def default_company(): return Transaction().context.get('company') @staticmethod def default_account_journal(): Journal = Pool().get('account.journal') journals = Journal.search([ ('type', '=', 'asset'), ]) if len(journals) == 1: return journals[0].id return None @fields.depends('company') def on_change_with_currency_digits(self, name=None): if self.company: return self.company.currency.digits return 2 @fields.depends('supplier_invoice_line', 'unit') def on_change_supplier_invoice_line(self): pool = Pool() Currency = pool.get('currency.currency') Unit = Pool().get('product.uom') if not self.supplier_invoice_line: self.quantity = None self.value = None self.start_date = self.default_start_date() return invoice_line = self.supplier_invoice_line invoice = invoice_line.invoice if invoice.company.currency != invoice.currency: with Transaction().set_context(date=invoice.currency_date): self.value = Currency.compute(invoice.currency, invoice_line.amount, invoice.company.currency) else: self.value = invoice_line.amount if invoice.invoice_date: self.purchase_date = invoice.invoice_date self.start_date = invoice.invoice_date if invoice_line.product.depreciation_duration: duration = relativedelta.relativedelta(months=int( invoice_line.product.depreciation_duration), days=-1) self.end_date = self.start_date + duration if not self.unit: self.quantity = invoice_line.quantity else: self.quantity = Unit.compute_qty(invoice_line.unit, invoice_line.quantity, self.unit) @fields.depends('product') def on_change_with_unit(self): if not self.product: return None return self.product.default_uom.id @fields.depends('unit') def on_change_with_unit_digits(self, name=None): if not self.unit: return 2 return self.unit.digits @fields.depends('end_date', 'product', 'start_date') def on_change_with_end_date(self): if all(getattr(self, k, None) for k in ('product', 'start_date')): if self.product.depreciation_duration: duration = relativedelta.relativedelta(months=int( self.product.depreciation_duration), days=-1) return self.start_date + duration return self.end_date @classmethod def get_customer_invoice_line(cls, assets, name): InvoiceLine = Pool().get('account.invoice.line') invoice_lines = InvoiceLine.search([ ('asset', 'in', [a.id for a in assets]), ]) result = dict((a.id, None) for a in assets) result.update(dict((l.asset.id, l.id) for l in invoice_lines)) return result def get_depreciated_amount(self): lines = [ line.accumulated_depreciation for line in self.lines if line.move and line.move.state == 'posted' ] return max(lines) if lines else 0 def compute_move_dates(self): """ Returns all the remaining dates at which asset depreciation movement will be issued. """ start_date = max([self.start_date] + [l.date for l in self.lines]) delta = relativedelta.relativedelta(self.end_date, start_date) # dateutil >= 2.0 has replace __nonzero__ by __bool__ which doesn't # work in Python < 3 if delta == relativedelta.relativedelta(): return [self.end_date] if self.frequency == 'monthly': rule = rrule.rrule(rrule.MONTHLY, dtstart=self.start_date, bymonthday=-1) elif self.frequency == 'yearly': rule = rrule.rrule(rrule.YEARLY, dtstart=self.start_date, bymonth=12, bymonthday=-1) dates = [ d.date() for d in rule.between(date2datetime(start_date), date2datetime(self.end_date)) ] dates.append(self.end_date) return dates def compute_depreciation(self, date, dates): """ Returns the depreciation amount for an asset on a certain date. """ amount = (self.value - self.get_depreciated_amount() - self.residual_value) if self.depreciation_method == 'linear': start_date = max( [self.start_date - relativedelta.relativedelta(days=1)] + [l.date for l in self.lines]) first_delta = dates[0] - start_date if len(dates) > 1: last_delta = dates[-1] - dates[-2] else: last_delta = first_delta if self.frequency == 'monthly': _, first_ndays = calendar.monthrange(dates[0].year, dates[0].month) _, last_ndays = calendar.monthrange(dates[-1].year, dates[-1].month) elif self.frequency == 'yearly': first_ndays = 365 + calendar.isleap(dates[0].year) last_ndays = 365 + calendar.isleap(dates[-1].year) first_ratio = Decimal(first_delta.days) / Decimal(first_ndays) last_ratio = Decimal(last_delta.days) / Decimal(last_ndays) depreciation = amount / (len(dates) - 2 + first_ratio + last_ratio) if date == dates[0]: depreciation *= first_ratio elif date == dates[-1]: depreciation *= last_ratio return self.company.currency.round(depreciation) def depreciate(self): """ Returns all the depreciation amounts still to be accounted. """ Line = Pool().get('account.asset.line') amounts = {} dates = self.compute_move_dates() amount = (self.value - self.get_depreciated_amount() - self.residual_value) if amount <= 0: return amounts residual_value, acc_depreciation = amount, Decimal(0) asset_line = None for date in dates: depreciation = self.compute_depreciation(date, dates) amounts[date] = asset_line = Line( acquired_value=self.value, depreciable_basis=amount, ) if depreciation > residual_value: asset_line.depreciation = residual_value asset_line.accumulated_depreciation = ( self.get_depreciated_amount() + acc_depreciation + residual_value) break else: residual_value -= depreciation acc_depreciation += depreciation asset_line.depreciation = depreciation asset_line.accumulated_depreciation = ( self.get_depreciated_amount() + acc_depreciation) else: if residual_value > 0 and asset_line is not None: asset_line.depreciation += residual_value asset_line.accumulated_depreciation += residual_value for asset_line in amounts.itervalues(): asset_line.actual_value = (self.value - asset_line.accumulated_depreciation) return amounts @classmethod @ModelView.button def create_lines(cls, assets): cls.clear_lines(assets) for asset in assets: for date, line in asset.depreciate().iteritems(): line.asset = asset.id line.date = date line.save() @classmethod @ModelView.button def clear_lines(cls, assets): Line = Pool().get('account.asset.line') lines_to_delete = [] for asset in assets: for line in asset.lines: if not line.move or line.move.state != 'posted': lines_to_delete.append(line) Line.delete(lines_to_delete) @classmethod @ModelView.button_action('account_asset.wizard_update') def update(cls, assets): pass def get_move(self, line): """ Return the account.move generated by an asset line. """ pool = Pool() Period = pool.get('account.period') Move = pool.get('account.move') MoveLine = pool.get('account.move.line') period_id = Period.find(self.company.id, line.date) expense_line = MoveLine( credit=0, debit=line.depreciation, account=self.product.account_expense_used, ) depreciation_line = MoveLine( debit=0, credit=line.depreciation, account=self.product.account_depreciation_used, ) return Move( company=self.company, origin=line, period=period_id, journal=self.account_journal, date=line.date, lines=[expense_line, depreciation_line], ) @classmethod def create_moves(cls, assets, date): """ Creates all account move on assets before a date. """ pool = Pool() Move = pool.get('account.move') Line = pool.get('account.asset.line') moves = [] lines = [] for asset_ids in grouped_slice(assets): lines += Line.search([ ('asset', 'in', list(asset_ids)), ('date', '<=', date), ('move', '=', None), ]) for line in lines: move = line.asset.get_move(line) move.save() moves.append(move) Line.write([line], { 'move': move.id, }) Move.post(moves) def get_closing_move(self, account): """ Returns closing move values. """ pool = Pool() Period = pool.get('account.period') Date = pool.get('ir.date') Move = pool.get('account.move') MoveLine = pool.get('account.move.line') date = Date.today() period_id = Period.find(self.company.id, date) if self.supplier_invoice_line: account_asset = self.supplier_invoice_line.account else: account_asset = self.product.account_asset_used asset_line = MoveLine( debit=0, credit=self.value, account=account_asset, ) depreciation_line = MoveLine( debit=self.get_depreciated_amount(), credit=0, account=self.product.account_depreciation_used, ) lines = [asset_line, depreciation_line] square_amount = asset_line.credit - depreciation_line.debit if square_amount: if not account: account = self.product.account_revenue_used counter_part_line = MoveLine( debit=square_amount if square_amount > 0 else 0, credit=-square_amount if square_amount < 0 else 0, account=account, ) lines.append(counter_part_line) return Move( company=self.company, origin=self, period=period_id, journal=self.account_journal, date=date, lines=lines, ) @classmethod def set_number(cls, assets): ''' Fill the number field with asset sequence. ''' pool = Pool() Sequence = pool.get('ir.sequence') Config = pool.get('account.configuration') config = Config(1) for asset in assets: if asset.number: continue asset.number = Sequence.get_id(config.asset_sequence.id) cls.save(assets) @classmethod @ModelView.button @Workflow.transition('running') def run(cls, assets): cls.set_number(assets) cls.create_lines(assets) @classmethod @ModelView.button @Workflow.transition('closed') def close(cls, assets, account=None): """ Close the assets. If account is provided, it will be used instead of the expense account. """ Move = Pool().get('account.move') cls.clear_lines(assets) moves = [] for asset in assets: move = asset.get_closing_move(account) move.save() moves.append(move) cls.write([asset], { 'move': move.id, }) Move.post(moves) def get_rec_name(self, name): return '%s - %s' % (self.number, self.product.rec_name) @classmethod def search_rec_name(cls, name, clause): names = clause[2].split(' - ', 1) res = [('number', clause[1], names[0])] if len(names) != 1 and names[1]: res.append(('product', clause[1], names[1])) return res @classmethod def copy(cls, assets, default=None): if default is None: default = {} default = default.copy() default.setdefault('lines', []) default.setdefault('state', 'draft') default.setdefault('number', None) default.setdefault('supplier_invoice_line', None) return super(Asset, cls).copy(assets, default=default) @classmethod def delete(cls, assets): for asset in assets: if asset.state != 'draft': cls.raise_user_error('delete_draft', asset.rec_name) return super(Asset, cls).delete(assets)
class InpatientMedication(ModelSQL, ModelView): 'Inpatient Medication' __name__ = 'gnuhealth.inpatient.medication' name = fields.Many2One('gnuhealth.inpatient.registration', 'Registration Code') medicament = fields.Many2One('gnuhealth.medicament', 'Medicament', required=True, help='Prescribed Medicament') indication = fields.Many2One( 'gnuhealth.pathology', 'Indication', help='Choose a disease for this medicament from the disease list. It' ' can be an existing disease of the patient or a prophylactic.') start_treatment = fields.DateTime('Start', help='Date of start of Treatment', required=True) end_treatment = fields.DateTime('End', help='Date of start of Treatment') dose = fields.Float('Dose', help='Amount of medication (eg, 250 mg) per dose', required=True) dose_unit = fields.Many2One( 'gnuhealth.dose.unit', 'dose unit', required=True, help='Unit of measure for the medication to be taken') route = fields.Many2One('gnuhealth.drug.route', 'Administration Route', required=True, help='Drug administration route code.') form = fields.Many2One('gnuhealth.drug.form', 'Form', required=True, help='Drug form, such as tablet or gel') qty = fields.Integer( 'x', required=True, help='Quantity of units (eg, 2 capsules) of the medicament') common_dosage = fields.Many2One( 'gnuhealth.medication.dosage', 'Frequency', help='Common / standard dosage frequency for this medicament') admin_times = fields.One2Many('gnuhealth.inpatient.medication.admin_time', 'name', "Admin times") log_history = fields.One2Many('gnuhealth.inpatient.medication.log', 'name', "Log History") frequency = fields.Integer( 'Frequency', help='Time in between doses the patient must wait (ie, for 1 pill' ' each 8 hours, put here 8 and select \"hours\" in the unit field') frequency_unit = fields.Selection([ (None, ''), ('seconds', 'seconds'), ('minutes', 'minutes'), ('hours', 'hours'), ('days', 'days'), ('weeks', 'weeks'), ('wr', 'when required'), ], 'unit', select=True, sort=False) frequency_prn = fields.Boolean('PRN', help='Use it as needed, pro re nata') is_active = fields.Boolean( 'Active', help='Check if the patient is currently taking the medication') discontinued = fields.Boolean('Discontinued') course_completed = fields.Boolean('Course Completed') discontinued_reason = fields.Char( 'Reason for discontinuation', states={ 'invisible': Not(Bool(Eval('discontinued'))), 'required': Bool(Eval('discontinued')), }, depends=['discontinued'], help='Short description for discontinuing the treatment') adverse_reaction = fields.Text( 'Adverse Reactions', help='Side effects or adverse reactions that the patient experienced') @fields.depends('discontinued', 'course_completed') def on_change_with_is_active(self): is_active = True if (self.discontinued or self.course_completed): is_active = False return is_active @fields.depends('is_active', 'course_completed') def on_change_with_discontinued(self): return not (self.is_active or self.course_completed) @fields.depends('is_active', 'discontinued') def on_change_with_course_completed(self): return not (self.is_active or self.discontinued) @staticmethod def default_is_active(): return True
class PurchaseLine(ModelSQL, ModelView): """ Fleet Management Purchase Line """ _name = 'purchase.line' product_fleet_type = fields.Function( fields.Char('Product_Fleet_Type', on_change_with=['product']), 'get_product_fleet_type') asset = fields.Many2One("fleet.asset", "Asset", states={ 'invisible': Not(Equal(Eval('product_fleet_type'), 'fuel')), 'required': Equal(Eval('product_fleet_type'), 'fuel') }, depends=['product_fleet_type']) meter_reading = fields.BigInteger( "Meter Reading", states={ 'invisible': Not(Equal(Eval('product_fleet_type'), 'fuel')), 'required': (Eval('product_fleet_type') == 'fuel') }, depends=['product_fleet_type']) fuel_efficiency = fields.Function( fields.Float("Fuel Efficiency", states={ 'invisible': Not(Equal(Eval('product_fleet_type'), 'fuel')), }, depends=['product_fleet_type']), 'get_fuel_efficiency') def get_product_fleet_type(self, ids, name): """Get the product type. """ res = {} for purchase_line in self.browse(ids): res[purchase_line.id] = purchase_line.product.fleet_management_type return res def get_fuel_efficiency(self, ids, name): """Return the fuel efficiency """ res = {} for purchase_line in self.browse(ids): efficiency = 0.00 previous_line_ids = self.search( [('asset', '=', purchase_line.asset.id), ('purchase.purchase_date', '<', purchase_line.purchase.purchase_date)], limit=1) if previous_line_ids: previous_line = self.browse(previous_line_ids[0]) efficiency = (purchase_line.meter_reading - \ previous_line.meter_reading) / purchase_line.quantity res[purchase_line.id] = efficiency return res def on_change_with_product_fleet_type(self, vals): """Set the value of product_fleet_type so that the invisible and required property may be set """ product_obj = Pool().get('product.product') if vals.get('product'): product = product_obj.browse(vals['product']) return product.fleet_management_type return None
class Exam(Workflow, ModelSQL, ModelView): '''Exam''' __name__ = 'exam_section.exam' state = fields.Selection([ ('draft', 'Draft'), ('confirm', 'Confirm'), ('approved', 'Approved'), ('in_progress', 'In Progress'), ('awaiting_ta_da', 'Awaiiting TA/DA Bills'), ('ta_da_submit', 'TA/DA Submit'), ('approval', 'Approval'), ('budget_allocation', 'Budget Allocation'), ('final_process', 'Final Process') ], 'Status', readonly=True) name = fields.Char('Exam Name') exam_type = fields.Many2One( 'exam_section.exam_type', 'Exam Type', states={ 'readonly': ~Eval('state').in_(['draft']) }, depends=['state'], required=True) date_from = fields.Date('Date From', states={ 'readonly': ~Eval('state').in_(['draft']) }, depends=['state'], required=True) date_to = fields.Date('Date To', states={ 'readonly': ~Eval('state').in_(['draft']) }, depends=['state'], required=True) centers = fields.One2Many('exam.centers', 'exam', 'Centers', states={ 'readonly': ~Eval('state').in_(['draft']) }, depends=['state']) employees = fields.One2Many('exam.employees', 'exam', 'Employees', states={ 'readonly': ~Eval('state').in_(['draft']) }, depends=['state']) renumeration_bills = fields.One2Many( 'exam_section.renumeration_bill', 'exam', 'Renumeration Bills', states={ 'readonly': ~Eval('state').in_(['draft']), 'invisible': ~Eval('state').in_([ 'draft', 'confirm', 'approved', 'in_progress' ]), }, depends=['state'] ) ta_da_bills = fields.One2Many( 'exam_section.ta_da_bill', 'exam', 'TA/DA Bills', states={ 'readonly': ~Eval('state').in_(['draft']), 'invisible': ~Eval('state').in_([ 'draft', 'confirm', 'approved', 'in_progress' ]), }, depends=['state'] ) total_renumeration_cost = fields.Function( fields.Float('Total Renumeration Cost'), 'get_total_renumeration') total_ta_da_cost = fields.Function( fields.Float('Total TA/DA Cost'), 'get_total_ta_da') def get_total_renumeration(self, name): res = 0 if self.employees: for employee in self.employees: if employee.renumeration_bill: res += employee.renumeration_bill.net_amount return res def get_total_ta_da(self, name): res = 0 if self.employees: for employee in self.employees: if employee.ta_da_bill: res += employee.ta_da_bill.total_amount return res @fields.depends('exam_type', 'date_from') def on_change_with_name(self): return '{} {}'.format( self.exam_type.name if self.exam_type else '', self.date_from.year if self.date_from else '', ) @classmethod def __setup__(cls): super().__setup__() cls._transitions = set(( ('draft', 'confirm'), ('confirm', 'approved'), ('approved', 'in_progress'), ('in_progress', 'awaiting_ta_da'), ('awaiting_ta_da', 'ta_da_submit'), ('ta_da_submit', 'approval'), ('approval', 'budget_allocation'), ('budget_allocation', 'final_process') )) cls._buttons.update({ 'confirm_data': { 'invisible': ~Eval('state').in_(['draft']) }, 'approve': { 'invisible': ~Eval('state').in_(['confirm']) }, 'send_in_progress': { 'invisible': ~Eval('state').in_(['approved']) }, 'generate_ta_da_bills': { 'invisible': ~Eval('state').in_(['in_progress']) }, 'submit_ta_da_bills': { 'invisible': ~Eval('state').in_(['awaiting_ta_da']) }, 'approve_ta_da': { 'invisible': ~Eval('state').in_(['ta_da_submit']) }, 'send_for_budget_allocation': { 'invisible': ~Eval('state').in_(['approval']) }, 'send_for_final_process': { 'invisible': ~Eval('state').in_(['budget_allocation']) } }) @staticmethod def default_state(): return 'draft' @staticmethod def get_current_exam(): exam_record = list(Transaction().timestamp.keys())[0] exam_table_and_id = exam_record.split(',') exam_table = exam_table_and_id[0] exam_id = exam_table_and_id[1] pool = Pool() Exam = pool.get(exam_table) current_exam = Exam.search([('id', '=', exam_id)])[0] return current_exam @classmethod @Workflow.transition('confirm') def confirm_data(cls, records): pass @classmethod @Workflow.transition('approved') def approve(cls, records): current_exam = cls.get_current_exam() Renum = Pool().get('exam_section.renumeration_bill') for current_employee in current_exam.employees: renum_bill_for_employee = Renum.create([{ 'type_of_examiner': 'internal', 'exam': current_exam, 'employee': current_employee.employee, 'designation': current_employee.employee.designation.name, }])[0] current_employee.renumeration_bill = renum_bill_for_employee current_employee.renumeration_bill.save() current_employee.save() @classmethod @Workflow.transition('in_progress') def send_in_progress(cls, records): pass @classmethod @Workflow.transition('awaiting_ta_da') def generate_ta_da_bills(cls, records): TADA = Pool().get('exam_section.ta_da_bill') current_exam = cls.get_current_exam() current_date_from = current_exam.date_from current_date_to = current_exam.date_to for current_employee in current_exam.employees: ta_da_bill_for_employee = TADA.create([{ 'employee': current_employee.employee, 'designation': current_employee.employee.designation, 'department': current_employee.employee.department, 'exam': current_exam, 'purpose': 'On deputation for {} examination from {} to {}'.format( current_exam.name, current_date_from.strftime("%d %B %Y"), current_date_to.strftime("%d %B %Y") ) }])[0] current_employee.ta_da_bill = ta_da_bill_for_employee current_employee.ta_da_bill.save() current_employee.save() @classmethod @Workflow.transition('ta_da_submit') def submit_ta_da_bills(cls, records): pass @classmethod @Workflow.transition('approval') def approve_ta_da(cls, records): pass @classmethod @Workflow.transition('budget_allocation') def send_for_budget_allocation(cls, records): pass @classmethod @Workflow.transition('final_process') def send_for_final_process(cls, records): pass
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) num_installment = 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) 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'], ) @staticmethod def default_state(): return 'draft' @classmethod def view_attributes(cls): 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 @classmethod def __setup__(cls): 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({ 'submitted_to_ao': { '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 @ModelView.button @Workflow.transition('forwarded_to_jo') def submitted_to_ao(cls, records): cls.loan_installment(records) pass @classmethod @ModelView.button @Workflow.transition('forwarded_to_ao') def forward_to_jo(cls, records): pass @classmethod @ModelView.button @Workflow.transition('approve') def forward_to_ao(cls, records): pass @classmethod @ModelView.button @Workflow.transition('cancel') def cancel(cls, records): 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): count = 0 LoanLine = Pool().get('hba.loan.line') for loan in records: amount = (loan.amount_required / loan.num_installment) for line in range(1, int(loan.num_installment) + 1): mydate = datetime.datetime.now().month month = mydate - 1 if month + line > 12: count += 1 if count > 12: count = 1 months = datetime.date(1900, count, 1).strftime('%B') else: months = datetime.date(1900, month + line, 1).strftime('%B') vals = { 'month': months, 'amount': amount, 'status': 'pending', 'loan': loan.id } line = LoanLine.create([vals])
class BudgetLineChange(ModelSQL, ModelView): 'General Budget Lines Changes' _name = 'ekd.account.budget.line.change' _description = __doc__ budget_line = fields.Many2One('ekd.account.budget.line', 'Budget Line', ondelete="RESTRICT") budget = fields.Many2One('ekd.account.budget', 'Budget', ondelete="RESTRICT") date_change = fields.Date('Date Change', required=True) direct_line = fields.Function(fields.Char('Direct Budget'), 'get_fields') income = fields.Function(fields.Boolean('Income Budget'), 'get_fields') kind = fields.Function(fields.Char('Kind'), 'get_fields') type_line = fields.Function(fields.Char('Type Line'), 'get_fields') analytic = fields.Function( fields.Many2One('ekd.account.analytic', 'Analytic Account'), 'get_fields') name = fields.Function(fields.Char('Name'), 'get_fields') note = fields.Text('Note') product = fields.Many2One('product.product', 'Product') quantity = fields.Float('Quantity', digits=(16, Eval('unit_digits', 2)), on_change=['quantity', 'price_unit'], states={ 'invisible': Or(Equal(Eval('kind'), 'amount'), Not(Equal(Eval('type_line'), 'line'))), 'tree_invisible': Or(Equal(Eval('kind'), 'amount'), Equal(Eval('type_line'), 'line')) }, depends=['kind', 'type_line']) uom = fields.Many2One('product.uom', 'Unit', states={ 'invisible': Equal(Eval('kind'), 'amount'), 'tree_invisible': Equal(Eval('kind'), 'amount') }, depends=['kind', 'type_line']) unit_digits = fields.Function( fields.Integer('Unit Digits', on_change_with=['uom']), 'get_unit_digits') price_unit = fields.Numeric('Price unit', digits=(16, 2), states={ 'invisible': Or(Equal(Eval('kind'), 'amount'), Not(Equal(Eval('type_line'), 'line'))), 'tree_invisible': Or(Equal(Eval('kind'), 'amount'), Not(Equal(Eval('type_line'), 'line'))) }, depends=['kind', 'type_line'], on_change=['quantity', 'price_unit']) amount = fields.Numeric( 'Amount', digits=(16, 2), states={'invisible': Equal(Eval('kind'), 'quantity')}) state = fields.Selection([('draft', 'Draft'), ('confirmed', 'Confirmed'), ('deleted', 'Deleted')], 'State', required=True, readonly=True) employee_confirm = fields.Many2One('res.user', 'Employee Confirmed') def __init__(self): super(BudgetLineChange, self).__init__() # self._sql_constraints += [ # ('code_uniq', 'UNIQUE(budget,sequence)', 'The sequence in the budget must be unique!'), # ] self._order.insert(0, ('budget_line', 'ASC')) def default_state(self): return 'draft' def default_budget(self): return Transaction().context.get('budget') or False def default_name(self): return Transaction().context.get('name') or False def default_uom(self): return Transaction().context.get('uom') or False def default_quantity(self): return Transaction().context.get('quantity') or False def default_price_unit(self): return Transaction().context.get('price_unit') or False def default_amount(self): return Transaction().context.get('amount') or False def get_unit_digits(self, ids, name): res = {} for line in self.browse(ids): if line.uom: res[line.id] = line.uom.digits else: res[line.id] = 2 return res def get_fields(self, ids, names): res = {} for line in self.browse(ids): if line.budget_line: for name in names: res.setdefault(name, {}) res[name].setdefault(line.id, False) if name == 'analytic': res[name][line.id] = line.budget_line.analytic.id elif name == 'name': res[name][line.id] = line.budget_line.name elif name == 'kind': res[name][line.id] = line.budget_line.kind elif name == 'direct_line': res[name][line.id] = line.budget_line.direct_line elif name == 'type_line': res[name][line.id] = line.budget_line.type_line return res def on_change_with_name(self, vals): if not vals.get('analytic'): return analytic_obj = self.pool.get('ekd.account.analytic') if not vals.get('name'): return analytic_obj.browse(vals.get('analytic')).rec_name def on_change_with_unit_digits(self, vals): if not vals.get('uom'): return 2 uom_obj = self.pool.get('product.uom') return uom_obj.browse(vals.get('uom')).digits def on_change_with_amount(self, vals): if not vals.get('quantity') or not vals.get('price_unit'): return vals return Decimal(str(vals.get('quantity'))) * Decimal( str(vals.get('price_unit'))) def on_change_quantity(self, vals): if not vals.get('quantity') or not vals.get('price_unit'): return vals return { 'amount': Decimal(str(vals.get('quantity'))) * Decimal(str(vals.get('price_unit'))) } def on_change_price_unit(self, vals): if not vals.get('quantity') or not vals.get('price_unit'): return vals return { 'amount': Decimal(str(vals.get('quantity'))) * Decimal(str(vals.get('price_unit'))) } def delete(self, ids): return super(BudgetLineChange, self).delete(ids) def create(self, vals): return super(BudgetLineChange, self).create(vals) def write(self, ids, vals): return super(BudgetLineChange, self).write(ids, vals)
class GPFSubscription(Workflow, ModelSQL, ModelView): 'GPF Subscription' __name__ = 'gpf.subscription' salary_code = fields.Char('Salary Code', states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state']) employee = fields.Many2One('company.employee', 'Employee', states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state']) doj = fields.Date('Date of Joining', states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state']) designation = fields.Many2One('employee.designation', 'Designation', states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state']) department = fields.Many2One('company.department', 'Department', states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state']) gpf_number = fields.Char('G.P.F.Number', states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state']) basic_pay = fields.Float('Basic Pay', states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state']) change_sub = fields.Selection( [ ('increase', 'Increase'), ('decrease', 'Decrease'), ], string='Change Subscription', required=True, sort=False, states={ 'readonly': ~Eval('state').in_(['draft', 'waiting_for_otp']), }, depends=['state']) pre_amount = fields.Float('Previous Amount', states={ 'readonly': ~Eval('state').in_(['draft']), }, depends=['state']) curr_amount = fields.Float( 'Current Amount', required=True, states={ 'readonly': ~Eval('state').in_(['draft', 'waiting_for_otp']), }, depends=['state']) otp = fields.Char( 'OTP', size=6, states={ 'invisible': ~Eval('state').in_(['waiting_for_otp']), }, depends=['state'], ) otp_generate = fields.Char('OTP Generated') otp_generate_time = fields.DateTime('OTP Generated Time') state = fields.Selection([ ('draft', 'Draft'), ('waiting_for_otp', 'Waiting For OTP'), ('submitted_to_ao', 'Forwarded to AO'), ('approved', 'Approved'), ('cancel_gpf', 'Cancel'), ], 'Status', readonly=True, sort=False) contract = fields.Many2One('hr.contract', 'Salary Details', states={'invisible': ~Eval('state').in_([])}) reason = fields.Char( 'Reason', states={ 'invisible': ~Eval('state').in_(['submitted_to_ao', 'cancel_gpf']), 'readonly': ~Eval('state').in_(['submitted_to_ao']), }, depends=['state'], ) approve_date = fields.Date( 'Date of Approve', states={ 'invisible': ~Eval('state').in_(['approved']), 'readonly': Eval('state').in_(['approved']), }, depends=['state'], ) gpf_balance = fields.Float('G.P.F.Amount') @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 @fields.depends('employee') def on_change_employee(self, name=None): if self.employee: self.salary_code = self.employee.salary_code self.doj = self.employee.date_of_joining self.designation = self.employee.designation self.department = self.employee.department self.gpf_number = self.employee.gpf_number pool = Pool() hrcontract = pool.get('hr.contract') contracts = hrcontract.search([('employee', '=', self.employee), ('active', '<=', True)]) for contract in contracts: self.contract = contract.id self.basic_pay = contract.basic self.pre_amount = contract.gpf_amount @staticmethod def default_state(): return 'draft' @staticmethod def default_change_sub(): return 'increase' @classmethod def validate(cls, records): super(GPFSubscription, cls).validate(records) for record in records: approvedate = datetime.now() - relativedelta(months=3) # gpf = cls.search([ # ('id', '!=', record.id), # ('employee', '=', record.employee), # ('approve_date', '>=', approvedate), # ('state', '=', 'approved') # ]) gpf_increase = cls.search([('id', '!=', record.id), ('employee', '=', record.employee), ('change_sub', '=', 'increase'), ('state', '=', 'approved')]) gpf_decrease = cls.search([('id', '!=', record.id), ('employee', '=', record.employee), ('change_sub', '=', 'decrease'), ('state', '=', 'approved')]) # if gpf: # cls.raise_user_error('Need for 3 Month GAP') if record.change_sub == 'increase' and len(gpf_increase) >= 2: cls.raise_user_error('Already 2 times increase') elif record.change_sub == 'decrease' and len(gpf_decrease) >= 1: cls.raise_user_error('Already 1 times decrease') record.valid_curr_amount() def valid_curr_amount(self): if self.basic_pay: basic = (6 * self.basic_pay) / 100 if self.basic_pay < self.curr_amount: self.raise_user_error('Current Amount maximum of basic amount') elif basic > self.curr_amount: self.raise_user_error('Current Amount minmum of basic amount 6%') elif (self.change_sub == 'increase' and self.pre_amount >= self.curr_amount): self.raise_user_error('Current Amount minmum of Previous amount') elif (self.change_sub == 'decrease' and self.pre_amount <= self.curr_amount): self.raise_user_error('Current Amount maximum of Previous amount') @classmethod def __setup__(cls): super().__setup__() cls._error_messages.update({ 'otp_error': "OTP is not fill", }) cls._transitions |= set(( ('draft', 'waiting_for_otp'), ('waiting_for_otp', 'submitted_to_ao'), ('submitted_to_ao', 'approved'), ('submitted_to_ao', 'cancel_gpf'), )) cls._buttons.update({ 'waiting_for_otp': { 'invisible': ~Eval('state').in_(['draft']), 'depends': ['state'], }, 'submitted_to_ao': { 'invisible': ~Eval('state').in_(['waiting_for_otp']), 'depends': ['state'], }, 'approved': { 'invisible': ~Eval('state').in_(['submitted_to_ao']), 'depends': ['state'], }, 'cancel_gpf': { 'invisible': ~Eval('state').in_(['submitted_to_ao']), 'depends': ['state'], }, }) @classmethod def create(cls, values): '''Overwrite the method to manage the parts.''' for vals in values: gpf_increase = cls.search([('employee', '=', vals['employee']), ('change_sub', '=', 'increase'), ('state', 'not in', ('approved', 'cancel_gpf'))]) gpf_decrease = cls.search([('employee', '=', vals['employee']), ('change_sub', '=', 'decrease'), ('state', 'not in', ('approved', 'cancel_gpf'))]) if gpf_increase: cls.raise_user_error('Please Approve Previous GPF') if gpf_decrease: cls.raise_user_error('Please Approve Previous GPF') return super().create(values) def notify_otp_to_user(self): message = """Dear {name}, OTP to digitally submitted_to_ao the GPF form is {otp}""".format(name=self.employee.party.name, otp=self.otp_generate) # TODO: Get the value of logged in user's employee. contact = self.employee.party.contact_mechanism_get('mobile') if not contact: self.raise_user_error("missing_contact") number = contact.value number = 9811292339 params = { 'msg': message + " (%s)" % contact.value, 'mobile': number, } url = "http://192.168.185.17/trytonsms/sendsms.aspx" requests.get(url, params) return True def generate_otp(self): # Generate OTP for the user self.otp_generate = ''.join( ["%s" % random.randint(0, 9) for num in range(0, 6)]) self.otp_generate_time = datetime.now() self.save() self.notify_otp_to_user() def validate_otp(self): # Validate OTP if self.otp != self.otp_generate: self.raise_user_error('invalid_otp') return True @classmethod @ModelView.button @Workflow.transition('waiting_for_otp') def waiting_for_otp(cls, records): for record in records: record.generate_otp() pass @classmethod @ModelView.button @Workflow.transition('submitted_to_ao') def submitted_to_ao(cls, records): for record in records: record.validate_otp() pass @classmethod @ModelView.button @Workflow.transition('approved') def approved(cls, records): cls.set_gpf_amount(records) @classmethod @ModelView.button @Workflow.transition('cancel_gpf') def cancel_gpf(cls, records): for record in records: if not record.reason: cls.raise_user_error('Please fill the reason') pass @classmethod def set_gpf_amount(cls, records): ''' Fill the number field with the sale sequence ''' pool = Pool() contract = pool.get('hr.contract') for gpf in records: if not gpf.otp: cls.raise_user_error('OTP is not fill') if gpf.contract: gpf.approve_date = datetime.now().date() gpf.save() contract.write([gpf.contract], { 'gpf_amount': gpf.curr_amount, })
class SaleLine(ModelSQL, ModelView): 'Sale Line' __name__ = 'sale.line' _rec_name = 'description' sale = fields.Many2One('sale.sale', 'Sale', ondelete='CASCADE', select=True) sequence = fields.Integer('Sequence') type = fields.Selection([ ('line', 'Line'), ], 'Type', select=True, required=True) quantity = fields.Float('Quantity', digits=(16, Eval('unit_digits', 2)), states={ 'invisible': Eval('type') != 'line', 'required': Eval('type') == 'line', 'readonly': ~Eval('_parent_sale', {}), }, depends=['type', 'unit_digits']) unit = fields.Many2One( 'product.uom', 'Unit', states={ 'required': Bool(Eval('product')), 'invisible': Eval('type') != 'line', 'readonly': ~Eval('_parent_sale', {}), }, domain=[ If(Bool(Eval('product_uom_category')), ('category', '=', Eval('product_uom_category')), ('category', '!=', -1)), ], depends=['product', 'type', 'product_uom_category']) unit_digits = fields.Function(fields.Integer('Unit Digits'), 'on_change_with_unit_digits') product = fields.Many2One('product.product', 'Product', states={ 'invisible': Eval('type') != 'line', 'readonly': ~Eval('_parent_sale', {}), }, depends=['type']) 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=(16, 4), states={ 'invisible': Eval('type') != 'line', 'required': Eval('type') == 'line', }, depends=['type']) amount = fields.Function( fields.Numeric('Amount', digits=(16, Eval('_parent_sale', {}).get('currency_digits', 2)), states={ 'invisible': ~Eval('type').in_(['line', 'subtotal']), 'readonly': ~Eval('_parent_sale'), }, depends=['type']), 'get_amount') description = fields.Text('Description', size=None, required=True) unit_price_w_tax = fields.Function( fields.Numeric('Unit Price with Tax', digits=(16, Eval('_parent_sale', {}).get('currency_digits', Eval('currency_digits', 2))), states={ 'invisible': Eval('type') != 'line', }, depends=['type', 'currency_digits']), 'get_price_with_tax') amount_w_tax = fields.Function( fields.Numeric('Amount with Tax', digits=(16, Eval('_parent_sale', {}).get('currency_digits', Eval('currency_digits', 2))), states={ 'invisible': ~Eval('type').in_(['line', 'subtotal']), }, depends=['type', 'currency_digits']), 'get_price_with_tax') currency_digits = fields.Function(fields.Integer('Currency Digits'), 'on_change_with_currency_digits') currency = fields.Many2One('currency.currency', 'Currency', states={ 'required': ~Eval('sale'), }, depends=['sale']) @classmethod def __setup__(cls): super(SaleLine, cls).__setup__() # Allow edit product, quantity and unit in lines without parent sale for fname in ('product', 'quantity', 'unit'): field = getattr(cls, fname) if field.states.get('readonly'): del field.states['readonly'] @staticmethod def default_type(): return 'line' @staticmethod def default_sale(): if Transaction().context.get('sale'): return Transaction().context.get('sale') return None @staticmethod def default_currency_digits(): Company = Pool().get('company.company') if Transaction().context.get('company'): company = Company(Transaction().context['company']) return company.currency.digits return 2 @staticmethod def default_currency(): Company = Pool().get('company.company') if Transaction().context.get('company'): company = Company(Transaction().context['company']) return company.currency.id @fields.depends('unit') def on_change_with_unit_digits(self, name=None): if self.unit: return self.unit.digits return 2 @fields.depends('product') def on_change_with_product_uom_category(self, name=None): if self.product: return self.product.default_uom_category.id def _get_context_sale_price(self): context = {} if getattr(self, 'sale', None): if getattr(self.sale, 'currency', None): context['currency'] = self.sale.currency.id if getattr(self.sale, 'party', None): context['customer'] = self.sale.party.id if getattr(self.sale, 'sale_date', None): context['sale_date'] = self.sale.sale_date if self.unit: context['uom'] = self.unit.id else: context['uom'] = self.product.default_uom.id return context @fields.depends('currency') def on_change_with_currency_digits(self, name=None): if self.currency: return self.currency.digits return 2 def get_amount(self, name): if self.type == 'line': return self.on_change_with_amount() elif self.type == 'subtotal': amount = Decimal('0.0') for line2 in self.sale.lines: if line2.type == 'line': amount += line2.sale.currency.round( Decimal(str(line2.quantity)) * line2.unit_price) elif line2.type == 'subtotal': if self == line2: break amount = Decimal('0.0') return amount return Decimal('0.0') @fields.depends('product', 'unit', 'quantity', 'description', '_parent_sale.party', '_parent_sale.currency', '_parent_sale.sale_date') def on_change_product(self): Product = Pool().get('product.product') if not self.product: return {} res = {} party = None party_context = {} if self.sale and self.sale.party: party = self.sale.party if party.lang: party_context['language'] = party.lang.code category = self.product.default_uom.category if not self.unit or self.unit not in category.uoms: res['unit'] = self.product.default_uom.id self.unit = self.product.default_uom res['unit.rec_name'] = self.product.default_uom.rec_name res['unit_digits'] = self.product.default_uom.digits with Transaction().set_context(self._get_context_sale_price()): res['unit_price'] = Product.get_sale_price([self.product], self.quantity or 0)[self.product.id] if res['unit_price']: res['unit_price'] = res['unit_price'].quantize( Decimal(1) / 10**self.__class__.unit_price.digits[1]) if not self.description: with Transaction().set_context(party_context): res['description'] = Product(self.product.id).rec_name self.unit_price = res['unit_price'] self.type = 'line' res['amount'] = self.on_change_with_amount() res['description'] = Product(self.product.id).name return res @fields.depends('product', 'quantity', 'unit', '_parent_sale.currency', '_parent_sale.party', '_parent_sale.sale_date') def on_change_quantity(self): Product = Pool().get('product.product') if not self.product: return {} res = {} with Transaction().set_context(self._get_context_sale_price()): res['unit_price'] = Product.get_sale_price([self.product], self.quantity or 0)[self.product.id] if res['unit_price']: res['unit_price'] = res['unit_price'].quantize( Decimal(1) / 10**self.__class__.unit_price.digits[1]) return res @fields.depends('type', 'quantity', 'unit_price', 'unit', '_parent_sale.currency') def on_change_with_amount(self): if self.type == 'line': currency = self.sale.currency if self.sale else None amount = Decimal(str(self.quantity or '0.0')) * \ (self.unit_price or Decimal('0.0')) if currency: return currency.round(amount) return amount return Decimal('0.0') @classmethod def get_price_with_tax(cls, lines, names): pool = Pool() amount_w_tax = {} unit_price_w_tax = {} def compute_amount_with_tax(line): if line.product.taxes_category == True: impuesto = line.product.category.taxes else: impuesto = line.product.taxes if impuesto == 'iva0': value = Decimal(0.0) elif impuesto == 'no_iva': value = Decimal(0.0) elif impuesto == 'iva12': value = Decimal(0.12) elif impuesto == 'iva14': value = Decimal(0.14) else: value = Decimal(0.0) tax_amount = line.unit_price * value return line.get_amount(None) + tax_amount for line in lines: amount = Decimal('0.0') unit_price = Decimal('0.0') currency = (line.sale.currency if line.sale else line.currency) if line.type == 'line': if line.quantity and line.product: amount = compute_amount_with_tax(line) unit_price = amount / Decimal(str(line.quantity)) elif line.product: old_quantity = line.quantity line.quantity = 1.0 unit_price = compute_amount_with_tax(line) line.quantity = old_quantity # Only compute subtotals if the two fields are provided to speed up if currency: amount = currency.round(amount) amount_w_tax[line.id] = amount unit_price_w_tax[line.id] = unit_price result = { 'amount_w_tax': amount_w_tax, 'unit_price_w_tax': unit_price_w_tax, } for key in result.keys(): if key not in names: del result[key] return result @fields.depends('type', 'unit_price', 'quantity', 'taxes', 'sale', '_parent_sale.currency', 'currency', 'product') def on_change_with_unit_price_w_tax(self, name=None): if not self.sale: self.sale = Transaction().context.get('sale') return SaleLine.get_price_with_tax( [self], ['unit_price_w_tax'])['unit_price_w_tax'][self.id] @fields.depends('type', 'unit_price', 'quantity', 'taxes', 'sale', '_parent_sale.currency', 'currency', 'product') def on_change_with_amount_w_tax(self, name=None): if not self.sale: self.sale = Transaction().context.get('sale') return SaleLine.get_price_with_tax( [self], ['amount_w_tax'])['amount_w_tax'][self.id]
class Work: __name__ = 'project.work' predecessors = fields.Many2Many('project.predecessor_successor', 'successor', 'predecessor', 'Predecessors', domain=[ ('parent', '=', Eval('parent')), ('id', '!=', Eval('id')), ], depends=['parent', 'id']) successors = fields.Many2Many('project.predecessor_successor', 'predecessor', 'successor', 'Successors', domain=[ ('parent', '=', Eval('parent')), ('id', '!=', Eval('id')), ], depends=['parent', 'id']) leveling_delay = fields.Float("Leveling Delay", required=True) back_leveling_delay = fields.Float("Back Leveling Delay", required=True) allocations = fields.One2Many('project.allocation', 'work', 'Allocations', states={ 'invisible': Eval('type') != 'task', }, depends=['type']) duration = fields.Function( fields.TimeDelta('Duration', 'company_work_time'), 'get_function_fields') early_start_time = fields.DateTime("Early Start Time", readonly=True) late_start_time = fields.DateTime("Late Start Time", readonly=True) early_finish_time = fields.DateTime("Early Finish Time", readonly=True) late_finish_time = fields.DateTime("Late Finish Time", readonly=True) actual_start_time = fields.DateTime("Actual Start Time") actual_finish_time = fields.DateTime("Actual Finish Time") constraint_start_time = fields.DateTime("Constraint Start Time") constraint_finish_time = fields.DateTime("Constraint Finish Time") early_start_date = fields.Function(fields.Date('Early Start'), 'get_function_fields') late_start_date = fields.Function(fields.Date('Late Start'), 'get_function_fields') early_finish_date = fields.Function(fields.Date('Early Finish'), 'get_function_fields') late_finish_date = fields.Function(fields.Date('Late Finish'), 'get_function_fields') actual_start_date = fields.Function(fields.Date('Actual Start'), 'get_function_fields', setter='set_function_fields') actual_finish_date = fields.Function(fields.Date('Actual Finish'), 'get_function_fields', setter='set_function_fields') constraint_start_date = fields.Function(fields.Date('Constraint Start', depends=['type']), 'get_function_fields', setter='set_function_fields') constraint_finish_date = fields.Function(fields.Date('Constraint Finish', depends=['type']), 'get_function_fields', setter='set_function_fields') @classmethod def __setup__(cls): super(Work, cls).__setup__() @classmethod def validate(cls, works): super(Work, cls).validate(works) cls.check_recursion(works, parent='successors') @staticmethod def default_leveling_delay(): return 0.0 @staticmethod def default_back_leveling_delay(): return 0.0 @classmethod def get_function_fields(cls, works, names): ''' Function to compute function fields ''' res = {} ids = [w.id for w in works] if 'duration' in names: all_works = cls.search([('parent', 'child_of', ids), ('active', '=', True)]) + works all_works = set(all_works) durations = {} id2work = {} leafs = set() for work in all_works: id2work[work.id] = work if not work.children: leafs.add(work.id) total_allocation = 0 if not work.allocations or not work.effort_duration: durations[work.id] = work.effort_duration continue for allocation in work.allocations: total_allocation += allocation.percentage durations[work.id] = datetime.timedelta( seconds=work.effort_duration.total_seconds() / (total_allocation / 100.0)) while leafs: for work_id in leafs: work = id2work[work_id] all_works.remove(work) if not work.active: continue if work.parent and work.parent.id in durations: durations[work.parent.id] += durations[work_id] next_leafs = set(w.id for w in all_works) for work in all_works: if not work.parent: continue if work.parent.id in next_leafs and work.parent in works: next_leafs.remove(work.parent.id) leafs = next_leafs res['duration'] = durations fun_fields = ('early_start_date', 'early_finish_date', 'late_start_date', 'late_finish_date', 'actual_start_date', 'actual_finish_date', 'constraint_start_date', 'constraint_finish_date') db_fields = ('early_start_time', 'early_finish_time', 'late_start_time', 'late_finish_time', 'actual_start_time', 'actual_finish_time', 'constraint_start_time', 'constraint_finish_time') for fun_field, db_field in zip(fun_fields, db_fields): if fun_field in names: values = {} for work in works: values[work.id] = getattr(work, db_field) \ and getattr(work, db_field).date() or None res[fun_field] = values return res @classmethod def set_function_fields(cls, works, name, value): fun_fields = ('actual_start_date', 'actual_finish_date', 'constraint_start_date', 'constraint_finish_date') db_fields = ('actual_start_time', 'actual_finish_time', 'constraint_start_time', 'constraint_finish_time') for fun_field, db_field in zip(fun_fields, db_fields): if fun_field == name: cls.write( works, { db_field: (value and datetime.datetime.combine(value, datetime.time()) or None), }) break @property def hours(self): if not self.duration: return 0 return self.duration.total_seconds() / 60 / 60 @classmethod def add_minutes(cls, company, date, minutes): minutes = int(round(minutes)) minutes = date.minute + minutes hours = minutes // 60 if hours: date = cls.add_hours(company, date, hours) minutes = minutes % 60 date = datetime.datetime(date.year, date.month, date.day, date.hour, minutes, date.second) return date @classmethod def add_hours(cls, company, date, hours): while hours: if hours != intfloor(hours): minutes = (hours - intfloor(hours)) * 60 date = cls.add_minutes(company, date, minutes) hours = intfloor(hours) hours = date.hour + hours days = hours // company.hours_per_work_day if days: date = cls.add_days(company, date, days) hours = hours % company.hours_per_work_day date = datetime.datetime(date.year, date.month, date.day, intfloor(hours), date.minute, date.second) hours = hours - intfloor(hours) return date @classmethod def add_days(cls, company, date, days): day_per_week = company.hours_per_work_week / company.hours_per_work_day while days: if days != intfloor(days): hours = (days - intfloor(days)) * company.hours_per_work_day date = cls.add_hours(company, date, hours) days = intfloor(days) days = date.weekday() + days weeks = days // day_per_week days = days % day_per_week if weeks: date = cls.add_weeks(company, date, weeks) date += datetime.timedelta(days=-date.weekday() + intfloor(days)) days = days - intfloor(days) return date @classmethod def add_weeks(cls, company, date, weeks): day_per_week = company.hours_per_work_week / company.hours_per_work_day if weeks != intfloor(weeks): days = (weeks - intfloor(weeks)) * day_per_week if days: date = cls.add_days(company, date, days) date += datetime.timedelta(days=7 * intfloor(weeks)) return date def compute_dates(self): values = {} get_early_finish = lambda work: values.get(work, {}).get( 'early_finish_time', work.early_finish_time) get_late_start = lambda work: values.get(work, {}).get( 'late_start_time', work.late_start_time) maxdate = lambda x, y: x and y and max(x, y) or x or y mindate = lambda x, y: x and y and min(x, y) or x or y # propagate constraint_start_time constraint_start = reduce(maxdate, (pred.early_finish_time for pred in self.predecessors), None) if constraint_start is None and self.parent: constraint_start = self.parent.early_start_time constraint_start = maxdate(constraint_start, self.constraint_start_time) works = deque([(self, constraint_start)]) work2children = {} parent = None while works or parent: if parent: work = parent parent = None # Compute early_finish if work.children: early_finish_time = reduce( maxdate, map(get_early_finish, work.children), None) else: early_finish_time = None if values[work]['early_start_time']: early_finish_time = self.add_hours( work.company, values[work]['early_start_time'], work.hours) values[work]['early_finish_time'] = early_finish_time # Propagate constraint_start on successors for w in work.successors: works.append((w, early_finish_time)) if not work.parent: continue # housecleaning work2children if work.parent not in work2children: work2children[work.parent] = set() work2children[work.parent].update(work.successors) if work in work2children[work.parent]: work2children[work.parent].remove(work) # if no sibling continue to walk up the tree if not work2children.get(work.parent): if work.parent not in values: values[work.parent] = {} parent = work.parent continue work, constraint_start = works.popleft() # take constraint define on the work into account constraint_start = maxdate(constraint_start, work.constraint_start_time) if constraint_start: early_start = self.add_hours(work.company, constraint_start, work.leveling_delay) else: early_start = None # update values if work not in values: values[work] = {} values[work]['early_start_time'] = early_start # Loop on children if they exist if work.children and work not in work2children: work2children[work] = set(work.children) # Propagate constraint_start on children for w in work.children: if w.predecessors: continue works.append((w, early_start)) else: parent = work # propagate constraint_finish_time constraint_finish = reduce(mindate, (succ.late_start_time for succ in self.successors), None) if constraint_finish is None and self.parent: constraint_finish = self.parent.late_finish_time constraint_finish = mindate(constraint_finish, self.constraint_finish_time) works = deque([(self, constraint_finish)]) work2children = {} parent = None while works or parent: if parent: work = parent parent = None # Compute late_start if work.children: reduce(mindate, map(get_late_start, work.children), None) else: late_start_time = None if values[work]['late_finish_time']: late_start_time = self.add_hours( work.company, values[work]['late_finish_time'], -work.hours) values[work]['late_start_time'] = late_start_time # Propagate constraint_finish on predecessors for w in work.predecessors: works.append((w, late_start_time)) if not work.parent: continue # housecleaning work2children if work.parent not in work2children: work2children[work.parent] = set() work2children[work.parent].update(work.predecessors) if work in work2children[work.parent]: work2children[work.parent].remove(work) # if no sibling continue to walk up the tree if not work2children.get(work.parent): if work.parent not in values: values[work.parent] = {} parent = work.parent continue work, constraint_finish = works.popleft() # take constraint define on the work into account constraint_finish = mindate(constraint_finish, work.constraint_finish_time) if constraint_finish: late_finish = self.add_hours(work.company, constraint_finish, -work.back_leveling_delay) else: late_finish = None # update values if work not in values: values[work] = {} values[work]['late_finish_time'] = late_finish # Loop on children if they exist if work.children and work not in work2children: work2children[work] = set(work.children) # Propagate constraint_start on children for w in work.children: if w.successors: continue works.append((w, late_finish)) else: parent = work # write values write_fields = ('early_start_time', 'early_finish_time', 'late_start_time', 'late_finish_time') for work, val in values.iteritems(): write_cond = False for field in write_fields: if field in val and getattr(work, field) != val[field]: write_cond = True break if write_cond: self.write([work], val) def reset_leveling(self): get_key = lambda w: (set(p.id for p in w.predecessors), set(s.id for s in w.successors)) parent_id = self.parent and self.parent.id or None siblings = self.search([('parent', '=', parent_id)]) to_clean = [] ref_key = get_key(self) for sibling in siblings: if sibling.leveling_delay == sibling.back_leveling_delay == 0: continue if get_key(sibling) == ref_key: to_clean.append(sibling) if to_clean: self.write(to_clean, { 'leveling_delay': 0, 'back_leveling_delay': 0, }) def create_leveling(self): # define some helper functions get_key = lambda w: (set(p.id for p in w.predecessors), set(s.id for s in w.successors)) over_alloc = lambda current_alloc, work: (reduce( lambda res, alloc: (res or (current_alloc[alloc.employee.id] + alloc.percentage) > 100 ), work.allocations, False)) def sum_allocs(current_alloc, work): res = defaultdict(float) for alloc in work.allocations: empl = alloc.employee.id res[empl] = current_alloc[empl] + alloc.percentage return res def compute_delays(siblings): # time_line is a list [[end_delay, allocations], ...], this # mean that allocations is valid between the preceding end_delay # (or 0 if it doesn't exist) and the current end_delay. timeline = [] for sibling in siblings: delay = 0 ignored = [] overloaded = [] item = None while timeline: # item is [end_delay, allocations] item = heappop(timeline) if over_alloc(item[1], sibling): ignored.extend(overloaded) ignored.append(item) delay = item[0] continue elif item[1] >= delay + sibling.duration: overloaded.append(item) else: # Succes! break heappush(timeline, [ delay + sibling.duration, sum_allocs(defaultdict(float), sibling), sibling.id ]) for i in ignored: heappush(timeline, i) for i in overloaded: i[1] = sum_allocs(i[1], sibling) heappush(timeline, i) yield sibling, delay siblings = self.search([('parent', '=', self.parent.id if self.parent else None)]) refkey = get_key(self) siblings = [s for s in siblings if get_key(s) == refkey] for sibling, delay in compute_delays(siblings): self.write([sibling], { 'leveling_delay': delay, }) siblings.reverse() for sibling, delay in compute_delays(siblings): self.write([sibling], { 'back_leveling_delay': delay, }) if self.parent: self.parent.compute_dates() @classmethod def write(cls, *args): super(Work, cls).write(*args) actions = iter(args) for works, values in zip(actions, actions): if 'effort' in values: for work in works: work.reset_leveling() fields = ('constraint_start_time', 'constraint_finish_time', 'effort') if reduce(lambda x, y: x or y in values, fields, False): for work in works: work.compute_dates() @classmethod def create(cls, vlist): works = super(Work, cls).create(vlist) for work in works: work.reset_leveling() work.compute_dates() return works @classmethod def delete(cls, works): to_update = set() for work in works: if work.parent and work.parent not in works: to_update.add(work.parent) to_update.update(c for c in work.parent.children if c not in works) super(Work, cls).delete(works) for work in to_update: work.reset_leveling() work.compute_dates()
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', 'Request for 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') 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 self.currency = self.request.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 InvestmentDeclaration(ModelSQL, ModelView): """ Investment Declaration""" __name__ = 'investment.declaration' name = fields.Char('Reference', help='Enter Reference') salary_code = fields.Char('Salary Code') employee = fields.Many2One('company.employee', 'Employee') gender = fields.Selection([('male', 'Male'), ('female', 'Female'), ('others', 'Others'), ('None', '')], 'Gender', help='Select Gender') pan_number = fields.Char('PAN No.') date_of_joining = fields.Date('Date of Joining') dob = fields.Date('Date of Birth') fiscal_year = fields.Many2One('account.fiscalyear', 'Fiscal Year') start_date = fields.Date('Starting Date') end_date = fields.Date('Ending Date') income_tax_assessment_year = fields.Char('Income Tax assessment year') declaration_lines = fields.One2Many( 'investment_declaration.line', 'declaration', 'Declarations', help='Fill the Investment Declarations') income_declaration_lines = fields.One2Many( 'hr.payroll.income.decalartion', 'declaration', 'Income Declaration', ) total_income_declared = fields.Function(fields.Float('Total'), 'getter_amount') net_tax_exempted = fields.Float('Total Tax Exempted', help="Under Chapter VI-A of IT Act") state = fields.Selection( [('draft', 'Draft'), ('awaiting_approval', 'Awaiting Approval'), ('approved', 'Approved'), ('awaiting_final_declaration', 'Awaiting Final Declaration'), ('closed', 'Closed')], 'Status', readonly=True, sort=False) # TODO: Review the state field @staticmethod def default_state(): '''returns draft state as default value''' return 'draft' @staticmethod def default_fiscal_year(): '''returns Current fiscal year as default value''' pool = Pool() fiscal = pool.get('account.fiscalyear') current_date = date.today() current_fiscal_year = fiscal.search( [('start_date', '<=', current_date), ('end_date', '>=', current_date)], limit=1) if len(current_fiscal_year) == 1: return current_fiscal_year[0].id @classmethod def getter_amount(cls, incomes, names): total_income = {} income_total = 0 for income in incomes: for income_line in income.income_declaration_lines: income_total += income_line.total_amount total_income[income.id] = income_total result = { 'total_income_declared': total_income, } return result @staticmethod def default_start_date(): ''' returns start_date as this year's current fiscal year's start_date value. ''' pool = Pool() fiscal = pool.get('account.fiscalyear') current_date = date.today() current_fiscal_year = fiscal.search( [('start_date', '<=', current_date), ('end_date', '>=', current_date)], limit=1) return current_fiscal_year[0].start_date @staticmethod def default_end_date(): ''' returns end_date as this year's current fiscal year's end_date value. ''' pool = Pool() fiscal = pool.get('account.fiscalyear') current_date = date.today() current_fiscal_year = fiscal.search( [('start_date', '<=', current_date), ('end_date', '>=', current_date)], limit=1) return current_fiscal_year[0].end_date @fields.depends('fiscal_year') def on_change_fiscal_year(self, name=None): if self.fiscal_year: self.income_tax_assessment_year = ( self.fiscal_year.income_tax_assessment_year) @staticmethod def default_employee(): ''' returns the logged in employee''' pool = Pool() User = pool.get('res.user') user = User(Transaction().user) employee = user.employee return employee.id if employee else None @fields.depends('employee') def on_change_employee(self): ''' Auto filling of fields based on employee and fiscal year ''' if self.employee: self.salary_code = self.employee.salary_code self.pan_number = self.employee.pan_number self.date_of_joining = self.employee.date_of_joining # self.gender=self.employee.gender # self.dob=self.employee.dob @classmethod def __setup__(cls): super().__setup__() cls._buttons.update({ 'calculate_net_tax_exempted': {}, }) @classmethod def calculate_net_tax_exempted(cls, investments): '''Calculate Net tax exempted Add the section wise total amount consider the section wise amount and find the maximum rebate allowed ''' investment_total = 0 dict_section_amount = {} for investment in investments: for line in investment.declaration_lines: if line.section not in dict_section_amount: dict_section_amount[line.section] = line.actual_amount else: dict_section_amount[line.section] += line.actual_amount for key, value in dict_section_amount.items(): investment_total += min([key.allowed_limit, value]) investment.net_tax_exempted = investment_total investment.save()
class ImportDataFloat(ModelSQL): "Import Data Float" __name__ = 'test.import_data.float' float = fields.Float('Float')
class Routing(metaclass=PoolMeta): __name__ = 'production.routing' supplier = fields.Many2One( 'party.party', "Supplier", help="The supplier to outsource the production.") supplier_service = fields.Many2One( 'product.product', "Service", ondelete='RESTRICT', domain=[ ('purchasable', '=', True), ('template.type', '=', 'service'), ], states={ 'required': Bool(Eval('supplier')), 'invisible': ~Eval('supplier'), }, depends=['supplier', 'supplier_service_supplier'], help="The service to buy to the supplier for the production.") supplier_service_supplier = fields.Many2One( 'purchase.product_supplier', "Supplier's Service", ondelete='RESTRICT', domain=[ ('template.type', '=', 'service'), If(Bool('supplier_service'), ['OR', [ ('template.products', '=', Eval('supplier_service')), ('product', '=', None), ], ('product', '=', Eval('supplier_service')), ], []), ('party', '=', Eval('supplier', -1)), ], states={ 'invisible': ~Eval('supplier'), }, depends=['supplier_service', 'supplier'], help="The supplier's service to buy for the production.") supplier_quantity = fields.Float("Quantity", states={ 'invisible': ~Eval('supplier_service'), 'required': Bool(Eval('supplier_service')), }, depends=['supplier_service'], help="The quantity to buy to produce one time the BOM.") @classmethod def default_supplier_quantity(cls): return 1 @fields.depends('supplier') def _get_product_supplier_pattern(self): return { 'party': self.supplier.id if self.supplier else -1, } @fields.depends('supplier', 'supplier_service', 'supplier_service_supplier') def on_change_supplier_service(self): if self.supplier_service: product_suppliers = list( self.supplier_service.product_suppliers_used( **self._get_product_supplier_pattern())) if len(product_suppliers) == 1: self.supplier_service_supplier, = product_suppliers elif (self.supplier_service_supplier and (self.supplier_service_supplier not in product_suppliers)): self.supplier_service = None @fields.depends('supplier_service', 'supplier_service_supplier') def on_change_supplier_service_supplier(self): if self.supplier_service_supplier: if self.supplier_service_supplier.product: self.supplier_service = self.supplier_service_supplier.product elif not self.supplier_service: products = self.supplier_service_supplier.template.products if len(products) == 1: self.supplier_service, = products @classmethod def view_attributes(cls): return super(Routing, cls).view_attributes() + [ ('//page[@id="supplier"]', 'states', { 'invisible': ~Eval('supplier'), }), ]
class ImportDataFloatRequired(ModelSQL): "Import Data Float Required" __name__ = 'test.import_data.float_required' float = fields.Float('Float', required=True)
class ShipmentCarrierMixin(PackageMixin): """ Mixin class which implements all the fields and methods required for getting shipping rates and generating labels """ is_international_shipping = fields.Function( fields.Boolean("Is International Shipping"), 'on_change_with_is_international_shipping') weight = fields.Function( fields.Float( "Weight", digits=(16, Eval('weight_digits', 2)), depends=['weight_digits'], ), 'get_weight') weight_uom = fields.Function(fields.Many2One('product.uom', 'Weight UOM'), 'get_weight_uom') weight_digits = fields.Function(fields.Integer('Weight Digits'), 'on_change_with_weight_digits') tracking_number = fields.Many2One( 'shipment.tracking', 'Tracking Number', select=True, states={'readonly': Eval('state') == 'done'}, depends=['state']) shipping_instructions = fields.Text('Shipping Instructions', states={ 'readonly': Eval('state').in_( ['cancel', 'done']), }, depends=['state']) available_carrier_services = fields.Function( fields.One2Many("carrier.service", None, "Available Carrier Services"), getter="on_change_with_available_carrier_services") carrier_service = fields.Many2One( "carrier.service", "Carrier Service", domain=[('id', 'in', Eval('available_carrier_services'))], depends=['available_carrier_services', 'state']) carrier_cost_method = fields.Function( fields.Char('Carrier Cost Method'), "on_change_with_carrier_cost_method") shipping_manifest = fields.Many2One("shipping.manifest", "Shipping Manifest", readonly=True, select=True) @property def carrier_cost_moves(self): "Moves to use for carrier cost calculation" return [] @fields.depends("carrier") def on_change_with_carrier_cost_method(self, name=None): if self.carrier: return self.carrier.carrier_cost_method @fields.depends('carrier') def on_change_with_available_carrier_services(self, name=None): if self.carrier: return map(int, self.carrier.services) return [] def get_weight(self, name=None): """ Returns sum of weight associated with each move line """ return sum( map(lambda move: move.get_weight(self.weight_uom, silent=True), self.carrier_cost_moves)) @fields.depends('weight_uom') def on_change_with_weight_digits(self, name=None): if self.weight_uom: return self.weight_uom.digits return 2 @classmethod def __setup__(cls): super(ShipmentCarrierMixin, cls).__setup__() cls.carrier_service.states = { 'readonly': Eval('state') == 'done', } cls._buttons.update({ 'label_wizard': { 'invisible': Or((~Eval('state').in_(['packed', 'done'])), (Bool(Eval('tracking_number')))), 'icon': 'tryton-executable', }, }) cls._error_messages.update({ 'no_shipments': 'There must be atleast one shipment.', 'too_many_shipments': 'The wizard can be called on only one shipment', 'tracking_number_already_present': 'Tracking Number is already present for this shipment.', 'invalid_state': 'Labels can only be generated when the ' 'shipment is in Packed or Done states only', 'wrong_carrier': 'Carrier for selected shipment is not of %s', 'no_packages': 'Shipment %s has no packages', 'warehouse_address_missing': 'Warehouse address is missing', }) # Following fields are already there in customer shipment, have # them in mixin so other shipment model can also use it. cls.carrier = fields.Many2One('carrier', 'Carrier', states={ 'readonly': Eval('state') == 'done', }, depends=['state']) cls.cost_currency = fields.Many2One('currency.currency', 'Cost Currency', states={ 'invisible': ~Eval('carrier'), 'required': Bool(Eval('carrier')), 'readonly': Eval('state') == 'done', }, depends=['carrier', 'state']) cls.cost_currency_digits = fields.Function( fields.Integer('Cost Currency Digits'), 'on_change_with_cost_currency_digits') cls.cost = fields.Numeric( 'Cost', digits=(16, Eval('cost_currency_digits', 2)), states={ 'invisible': ~Eval('carrier'), 'readonly': Eval('state') == 'done', }, depends=['carrier', 'state', 'cost_currency_digits']) @fields.depends('currency') def on_change_with_cost_currency_digits(self, name=None): if self.cost_currency: return self.cost_currency.digits return 2 @classmethod @ModelView.button_action('shipping.wizard_generate_shipping_label') def label_wizard(cls, shipments): if len(shipments) == 0: cls.raise_user_error('no_shipments') elif len(shipments) > 1: cls.raise_user_error('too_many_shipments') @classmethod def copy(cls, shipments, default=None): if default is None: default = {} default = default.copy() default['tracking_number'] = None return super(ShipmentCarrierMixin, cls).copy(shipments, default=default) @fields.depends('delivery_address', 'warehouse') def on_change_with_is_international_shipping(self, name=None): """ Return True if international shipping """ from_address = self._get_ship_from_address(silent=True) if self.delivery_address and from_address and \ from_address.country and self.delivery_address.country and \ from_address.country != self.delivery_address.country: return True return False def get_weight_uom(self, name): """ Returns weight uom for the shipment """ ModelData = Pool().get('ir.model.data') return ModelData.get_id('product', 'uom_pound') def _get_ship_from_address(self, silent=False): """ Usually the warehouse from which you ship """ if self.warehouse and self.warehouse.address: return self.warehouse.address if not silent: return self.raise_user_error('warehouse_address_missing') def allow_label_generation(self): """ Shipment must be in the right states and tracking number must not be present. """ if self.state not in ('packed', 'done'): self.raise_user_error('invalid_state') if self.tracking_number: self.raise_user_error('tracking_number_already_present') return True def _create_default_package(self, box_type=None): """ Create a single stock package for the whole shipment """ Package = Pool().get('stock.package') package, = Package.create([{ 'shipment': '%s,%d' % (self.__name__, self.id), 'box_type': box_type and box_type.id, 'moves': [('add', self.carrier_cost_moves)], }]) return package def get_shipping_rates(self, carriers=None, silent=False): """ Gives a list of rates from carriers provided. If no carriers provided, return rates from all the carriers. List contains dictionary with following minimum keys: [ { 'display_name': Name to display, 'carrier_service': carrier.service active record, 'cost': cost, 'cost_currency': currency.currency active repord, 'carrier': carrier active record, }.. ] """ Carrier = Pool().get('carrier') if carriers is None: carriers = Carrier.search([]) rates = [] for carrier in carriers: rates.extend(self.get_shipping_rate(carrier, silent)) return rates def get_shipping_rate(self, carrier, carrier_service=None, silent=False): """ Gives a list of rates from provided carrier and carrier service. List contains dictionary with following minimum keys: [ { 'display_name': Name to display, 'carrier_service': carrier.service active record, 'cost': cost, 'cost_currency': currency.currency active repord, 'carrier': carrier active record, }.. ] """ Company = Pool().get('company.company') if carrier.carrier_cost_method == 'product': currency = Company(Transaction().context['company']).currency rate_dict = { 'display_name': carrier.rec_name, 'carrier_service': carrier_service, 'cost': carrier.carrier_product.list_price, 'cost_currency': currency, 'carrier': carrier, } return [rate_dict] return [] def apply_shipping_rate(self, rate): """ This method applies shipping rate. Rate is a dictionary with following minimum keys: { 'display_name': Name to display, 'carrier_service': carrier.service active record, 'cost': cost, 'cost_currency': currency.currency active repord, 'carrier': carrier active record, } """ Currency = Pool().get('currency.currency') shipment_cost = rate['cost_currency'].round(rate['cost']) if self.cost_currency != rate['cost_currency']: shipment_cost = Currency.compute(rate['cost_currency'], shipment_cost, self.cost_currency) self.cost = shipment_cost self.cost_currency = self.cost_currency self.carrier = rate['carrier'] self.carrier_service = rate['carrier_service'] self.save() def generate_shipping_labels(self, **kwargs): """ Generates shipment label for shipment and saves labels, tracking numbers. """ self.raise_user_error( "Shipping label generation feature is not available") @staticmethod def default_cost_currency(): Company = Pool().get('company.company') company_id = Transaction().context.get('company') return company_id and Company(company_id).currency.id or None @property def ship_from_address(self): return None @property def ship_to_address(self): return None
class ChildernEductionAllowance(Workflow, ModelSQL, ModelView): """Childern Eduction Allowance for an Employee""" __name__ = 'hr.allowance.cea' salary_code = fields.Char('Salary Code', required=True) employee = fields.Many2One('company.employee', 'Employee Name', required=True) designation = fields.Many2One('employee.designation', 'Designation', required=True) department = fields.Many2One('company.department', 'Department', required=True) children_no = fields.Selection([ ('1', 'One'), ('2', 'Two'), ], 'Number of Children', required=True) amount = fields.Function(fields.Float('Education Amount', required=True), 'on_change_with_amount') from_date = fields.Date('From Date', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state']) to_date = fields.Date('To Date', states={'readonly': ~Eval('state').in_(['draft'])}, depends=['state']) state = fields.Selection([ ('draft', 'Draft'), ('submit', 'Submit'), ('cash_section_officer', 'Cash Section_officer'), ('cancel', 'Cancel'), ('approve', 'Approve'), ], 'Status', readonly=True) @staticmethod def default_state(): return 'draft' @staticmethod def default_from_date(): return datetime.date.today() @classmethod def __setup__(cls): super().__setup__() cls._buttons.update({ 'submit': { 'invisible': ~Eval('state').in_(['draft']) }, 'cash_section_officer_approval': { 'invisible': ~Eval('state').in_(['submit']) }, 'approve': { 'invisible': ~Eval('state').in_(['cash_section_officer']) }, 'cancel': { 'invisible': Eval('state').in_(['draft', 'approve', 'cancel']) }, }) cls._transitions |= set(( ('draft', 'submit'), ('submit', 'cash_section_officer'), ('submit', 'cancel'), ('cash_section_officer', 'approve'), ('cash_section_officer', 'cancel'), )) @classmethod @Workflow.transition('submit') def submit(cls, records): pass @classmethod @Workflow.transition('cash_section_officer') def cash_section_officer_approval(cls, records): pass @classmethod @Workflow.transition('approve') def approve(cls, records): pass @classmethod @Workflow.transition('cancel') def cancel(cls, records): pass @fields.depends('employee') def on_change_employee(self): if self.employee: self.salary_code = self.employee.salary_code if self.employee.salary_code else None self.designation = self.employee.designation if self.employee.designation else None self.department = self.employee.department if self.employee.department else None @fields.depends('children_no') def on_change_with_amount(self, name=None): res = 0 if self.children_no: if (self.children_no == '1'): res = 2250 elif (self.children_no == '2'): res = 4500 return res @staticmethod def default_employee(): global current_employee current_employee = None pool = Pool() Employee = pool.get('company.employee') employee_id = Transaction().context.get('employee') employee = Employee.search([('id', '=', employee_id)]) if employee != []: current_employee = employee[0] return current_employee.id if current_employee else None
class WorkStatus(DeactivableMixin, sequence_ordered(), ModelSQL, ModelView): 'Work Status' __name__ = 'project.work.status' _get_default_status_cache = Cache('project_work_status.get_default_status') _get_window_domains_cache = Cache('project_work_status.get_window_domains') types = fields.MultiSelection( 'get_types', "Types", help="The type of works which can use this status.") name = fields.Char("Name", required=True, translate=True) progress = fields.Float( "Progress", domain=['OR', ('progress', '=', None), [ ('progress', '>=', 0), ('progress', '<=', 1), ], ], help="The minimum progress required for this status.") default = fields.Boolean( "Default", help="Check to use as default status for the type.") count = fields.Boolean( "Count", help="Check to show the number of works in this status.") @classmethod def get_types(cls): pool = Pool() Work = pool.get('project.work') return Work.fields_get(['type'])['type']['selection'] @classmethod def get_default_status(cls, type_=None): if type_ is None: return None status = cls._get_default_status_cache.get(type_, -1) if status != -1: return status records = cls.search([ ('types', 'in', type_), ('default', '=', True) ], limit=1) if records: status = records[0].id else: status = None cls._get_default_status_cache.set(type, status) return status @classmethod def create(cls, vlist): cls._get_default_status_cache.clear() cls._get_window_domains_cache.clear() return super().create(vlist) @classmethod def write(cls, *args): super().write(*args) cls._get_default_status_cache.clear() cls._get_window_domains_cache.clear() @classmethod def delete(cls, status): cls._get_default_status_cache.clear() cls._get_window_domains_cache.clear() super().delete(status) @classmethod def get_window_domains(cls, action): pool = Pool() Data = pool.get('ir.model.data') if action.id == Data.get_id('project', 'act_project_tree'): return cls._get_window_domains([x[0] for x in cls.get_types()]) elif action.id == Data.get_id('project', 'act_project_form'): return cls._get_window_domains(['project']) elif action.id == Data.get_id('project', 'act_task_form'): return cls._get_window_domains(['task']) else: return [] @classmethod def _get_window_domains(cls, types): key = tuple(sorted(types)) domains = cls._get_window_domains_cache.get(key) if domains is not None: return domains encoder = PYSONEncoder() domains = [] for status in cls.search([('types', 'in', types)]): domain = encoder.encode([('status', '=', status.id)]) domains.append((status.name, domain, status.count)) if domains: domains.append( (gettext('project.msg_domain_all'), '[]', False)) cls._get_window_domains_cache.set(key, domains) return domains
class EstateAllotment(Workflow, ModelSQL, ModelView): """Employee Allotment""" __name__ = "estate.allotment" employee = fields.Many2One('company.employee', 'Employee', states={ 'readonly': ~Eval('state').in_(['draft']) }, depends=['state']) center_name = fields.Function( fields.Char('Center Name'), 'on_change_with_center_name' ) department_name = fields.Function( fields.Char('Department Name'), 'on_change_with_departemnt_name' ) salary_code = fields.Function( fields.Char('Salary Code'), 'on_change_with_salary_code' ) quarter_no = fields.Char('Quarter No.', states={ 'readonly': ~Eval('state').in_(['draft']) }, depends=['state']) location = fields.Selection( [ ('ansari_nagar_east', 'Ansari Nagar(E)'), ('ansari_nagar_west', 'Ansari Nagar(W)'), ('masjid_moth', 'Masjid Moth'), ('a_v_nagar_old', 'Ayurvigyan Nagar(Old)'), ('a_v_nagar_new', 'Ayurvigyan Nagar(New)'), ('asiad_village_complex', 'Asiad Village Complex'), ('ballabhgarh', 'Ballabhgarh'), ], 'Location', states={ 'readonly': ~Eval('state').in_(['draft']) }, depends=['state']) quarter_type = fields.Many2One( 'estate.quarter_type', 'Quarter Type', states={ 'readonly': ~Eval('state').in_(['draft']) }, depends=['state', 'location'], domain=[ ('locations', '=', Eval('location')) ]) state = fields.Selection([ ('draft', 'Draft'), ('confirm', 'Confirm'), ('submit', 'Submit') ], 'Status', required=True, readonly=True) garage = fields.Boolean( 'Garage?', states={ 'readonly': ~Eval('state').in_(['draft']) }, depends=['state']) servant_quarter = fields.Boolean( 'Servant Quarter?', states={ 'readonly': ~Eval('state').in_(['draft']) }, depends=['state']) date_of_allocation = fields.Date('Date of Allocation', states={ 'readonly': ~Eval('state').in_(['draft']) }, depends=['state']) date_of_occupation = fields.Date('Date of Occupation', states={ 'readonly': ~Eval('state').in_(['draft']) }, depends=['state']) date_of_vacation = fields.Date('Date of Vacation', states={ 'readonly': ~Eval('state').in_(['draft']) }, depends=['state']) license_fee = fields.Function( fields.Float('License Fee'), 'get_license_fee' ) water_charges = fields.Function( fields.Float('Water Charges'), 'get_water_charges' ) garage_fee = fields.Function( fields.Float('Garage'), 'on_change_with_garage_fee' ) servant_quarter_fee = fields.Function( fields.Float('Servant Quarter'), 'on_change_with_servant_quarter_fee' ) @staticmethod def default_state(): return 'draft' @staticmethod def default_location(): return 'ansari_nagar_east' @staticmethod def default_license_fee(): return 0 @staticmethod def default_water_charges(): return 0 @staticmethod def default_garage_fee(): return 0 @staticmethod def default_servant_quarter_fee(): return 0 @staticmethod def default_quarter_type(): return 1 def get_license_fee(self, name): if self.quarter_type: return self.quarter_type.license_fee else: return 0 return self.quarter_type.license_fee def get_water_charges(self, name): if self.quarter_type: return self.quarter_type.water_charges else: return 0 return self.quarter_type.water_charges @fields.depends('employee') def on_change_with_center_name(self, name=None): if self.employee and self.employee.center: return self.employee.center.name @fields.depends('employee') def on_change_with_departemnt_name(self, name=None): if self.employee and self.employee.department: return self.employee.department.name @fields.depends('employee') def on_change_with_salary_code(self, name=None): if self.employee and self.employee.salary_code: return self.employee.salary_code @fields.depends('garage') def on_change_with_garage_fee(self, name=None): # import pdb;pdb.set_trace() garage_fee = 0 if self.garage: garage_fee = 40 else: garage_fee = 0 return garage_fee @fields.depends('servant_quarter') def on_change_with_servant_quarter_fee(self, name=None): servant_quarter_fee = 0 if self.servant_quarter: servant_quarter_fee = 70 else: servant_quarter_fee = 0 return servant_quarter_fee @classmethod def __setup__(cls): super().__setup__() cls._transitions |= set(( ('draft', 'confirm') # ('confirm', 'submit') )) cls._buttons.update({ 'confirm_details': { 'invisible': ~Eval('state').in_(['draft']), 'depends': ['state'] } # 'submit': { # 'invisible': ~Eval('state').in_(['confirm']), # 'depends': ['state'] # } }) @classmethod # @ModelView.button @Workflow.transition('confirm') def confirm_details(cls, records): print('+++++++++++++++++++++++++++++++++++++')
class Float(ModelSQL): 'Float' __name__ = 'test.float' float = fields.Float(string='Float', help='Test float', required=False)
class SaleOpportunityReportMixin: number = fields.Integer('Number') converted = fields.Integer('Converted') conversion_rate = fields.Function( fields.Float('Conversion Rate', digits=(1, 4)), 'get_conversion_rate') won = fields.Integer('Won') winning_rate = fields.Function(fields.Float('Winning Rate', digits=(1, 4)), 'get_winning_rate') lost = fields.Integer('Lost') company = fields.Many2One('company.company', 'Company') currency = fields.Function( fields.Many2One('currency.currency', 'Currency'), 'get_currency') currency_digits = fields.Function(fields.Integer('Currency Digits'), 'get_currency_digits') amount = fields.Numeric('Amount', digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']) converted_amount = fields.Numeric('Converted Amount', digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']) conversion_amount_rate = fields.Function( fields.Float('Conversion Amount Rate', digits=(1, 4)), 'get_conversion_amount_rate') won_amount = fields.Numeric('Won Amount', digits=(16, Eval('currency_digits', 2)), depends=['currency_digits']) winning_amount_rate = fields.Function( fields.Float('Winning Amount Rate', digits=(1, 4)), 'get_winning_amount_rate') @staticmethod def _converted_state(): return ['converted', 'won'] @staticmethod def _won_state(): return ['won'] @staticmethod def _lost_state(): return ['lost'] def get_conversion_rate(self, name): if self.number: digits = getattr(self.__class__, name).digits[1] return round(float(self.converted) / self.number, digits) else: return 0.0 def get_winning_rate(self, name): if self.number: digits = getattr(self.__class__, name).digits[1] return round(float(self.won) / self.number, digits) else: return 0.0 def get_currency(self, name): return self.company.currency.id def get_currency_digits(self, name): return self.company.currency.digits def get_conversion_amount_rate(self, name): if self.amount: digits = getattr(self.__class__, name).digits[1] return round( float(self.converted_amount) / float(self.amount), digits) else: return 0.0 def get_winning_amount_rate(self, name): if self.amount: digits = getattr(self.__class__, name).digits[1] return round(float(self.won_amount) / float(self.amount), digits) else: return 0.0 @classmethod def table_query(cls): Opportunity = Pool().get('sale.opportunity') opportunity = Opportunity.__table__() return opportunity.select( Max(opportunity.create_uid).as_('create_uid'), Max(opportunity.create_date).as_('create_date'), Max(opportunity.write_uid).as_('write_uid'), Max(opportunity.write_date).as_('write_date'), opportunity.company, Count(Literal(1)).as_('number'), Sum( Case((opportunity.state.in_( cls._converted_state()), Literal(1)), else_=Literal(0))).as_('converted'), Sum( Case((opportunity.state.in_(cls._won_state()), Literal(1)), else_=Literal(0))).as_('won'), Sum( Case((opportunity.state.in_(cls._lost_state()), Literal(1)), else_=Literal(0))).as_('lost'), Sum(opportunity.amount).as_('amount'), Sum( Case((opportunity.state.in_( cls._converted_state()), opportunity.amount), else_=Literal(0))).as_('converted_amount'), Sum( Case((opportunity.state.in_( cls._won_state()), opportunity.amount), else_=Literal(0))).as_('won_amount'))