def _customize_taskline_fields(schema): """ Customize TaskLine colander schema related fields :param obj schema: The schema to modify """ schema.validator = tva_product_validator schema.after_bind = taskline_after_bind customize = functools.partial(forms.customize_field, schema) customize('id', widget=deform.widget.HiddenWidget()) customize( "description", widget=deform.widget.TextAreaWidget(), validator=forms.textarea_node_validator, ) customize("cost", typ=AmountType(5), missing=colander.required) customize("quantity", typ=QuantityType(), missing=colander.required) customize( "unity", validator=forms.get_deferred_select_validator(WorkUnit, id_key='label'), missing=colander.drop, ) customize( "tva", typ=AmountType(2), validator=forms.get_deferred_select_validator(Tva, id_key='value'), missing=colander.required, ) customize( "product_id", validator=forms.get_deferred_select_validator(Product), missing=colander.drop, ) return schema
def _customize_discountline_fields(schema): """ Customize DiscountLine colander schema related fields :param obj schema: The schema to modify """ customize = functools.partial(forms.customize_field, schema) customize("id", widget=deform.widget.HiddenWidget()) customize("task_id", missing=colander.required) customize("description", widget=deform.widget.TextAreaWidget(), validator=forms.textarea_node_validator) customize( "amount", typ=AmountType(5), missing=colander.required, ) customize( "tva", typ=AmountType(2), validator=forms.get_deferred_select_validator(Tva, id_key='value'), missing=colander.required, ) return schema
class ExpenseLine(BaseExpenseLine, ExpenseLineCompute): """ Common Expense line """ __tablename__ = "expense_line" __table_args__ = default_table_args __mapper_args__ = dict(polymorphic_identity='expenseline') id = Column(Integer, ForeignKey('baseexpense_line.id'), primary_key=True, info={'colanderalchemy': forms.EXCLUDED}) ht = Column( Integer, info={ 'colanderalchemy': { 'typ': AmountType(2), 'title': 'Montant HT', 'missing': colander.required, } }, ) tva = Column( Integer, info={ 'colanderalchemy': { 'typ': AmountType(2), 'title': 'Montant de la TVA', 'missing': colander.required, } }, ) sheet = relationship("ExpenseSheet", uselist=False, info={'colanderalchemy': forms.EXCLUDED}) def __json__(self, request): res = BaseExpenseLine.__json__(self, request) res.update( dict(ht=integer_to_amount(self.ht, 2), tva=integer_to_amount(self.tva, 2))) return res def duplicate(self, sheet=None): line = ExpenseLine() line.description = self.description line.category = self.category line.type_id = self.type_id line.ht = self.ht line.tva = self.tva return line
class SaleProduct(DBBASE): """ A product model """ __table_args__ = default_table_args __tablename__ = 'sale_product' id = Column(Integer, primary_key=True) label = Column(String(255), nullable=False) ref = Column(String(100), nullable=True) description = Column(Text(), default='') tva = Column( Integer, info={ 'colanderalchemy': { "title": u"Montant TVA (cache)", "typ": AmountType(2), }, 'export': forms.EXCLUDED, }, ) value = Column(Float(), default=0) unity = Column(String(100), default='') category_id = Column(ForeignKey('sale_product_category.id')) category = relationship( SaleProductCategory, backref=backref('products'), info={'colanderalchemy': forms.EXCLUDED}, ) product_id = Column(Integer) product = relationship("Product", primaryjoin="Product.id==SaleProduct.product_id", uselist=False, foreign_keys=product_id, info={'colanderalchemy': { 'exclude': True }}) def __json__(self, request): """ Json repr of our model """ return dict( id=self.id, label=self.label, ref=self.ref, description=self.description, tva=integer_to_amount(self.tva, 2), value=self.value, unity=self.unity, product_id=self.product_id, category_id=self.category_id, category=self.category.title, ) @property def company(self): return self.category.company
def _customize_payment_schema(schema): """ Add form schema customization to the given payment edition schema :param obj schema: The schema to edit """ customize = functools.partial(forms.customize_field, schema) customize("mode", validator=forms.get_deferred_select_validator(PaymentMode, id_key='label'), missing=colander.required) customize("amount", typ=AmountType(5), missing=colander.required) customize("bank_remittance_id", missing=colander.required) customize("date", missing=colander.required) customize("task_id", missing=colander.required) customize( "bank_id", validator=forms.get_deferred_select_validator(BankAccount), missing=colander.required, ) customize( "tva_id", validator=forms.get_deferred_select_validator(Tva), missing=colander.drop, ) customize("user_id", missing=colander.required) return schema
class ProductTaskLine(colander.MappingSchema): """ A single estimation line """ id = colander.SchemaNode(colander.Integer(), widget=deform.widget.HiddenWidget(), missing=u"", css_class="span0") description = colander.SchemaNode( colander.String(), widget=deform.widget.TextInputWidget(readonly=True), missing=u'', css_class='col-md-3', ) tva = colander.SchemaNode( AmountType(), widget=deform_extensions.DisabledInput(), css_class='col-md-1', title=u'TVA', ) product_id = colander.SchemaNode( colander.Integer(), widget=deferred_product_widget, validator=deferred_product_validator, missing="", css_class="col-md-2", title=u"Code produit", )
class AmountRangeSchema(colander.MappingSchema): """ Used to filter on a range of amount """ start = colander.SchemaNode( AmountType(5), title="", missing=colander.drop, description=u"TTC entre", ) end = colander.SchemaNode( AmountType(5), title="", missing=colander.drop, description=u"et", )
class TvaPayment(colander.MappingSchema): amount = colander.SchemaNode( AmountType(5), title=u"Montant de l'encaissement", ) tva_id = colander.SchemaNode(colander.Integer(), title=u"Tva liée à cet encaissement", widget=forms.get_deferred_select( Tva, mandatory=True, keys=('id', 'name')), validator=deferred_tva_id_validator)
def _customize_paymentline_schema(schema): """ Customize PaymentLine related form schema :param obj schema: The schema generated by colanderalchemy :rtype: `colander.SQLAlchemySchemaNode` """ customize = functools.partial(forms.customize_field, schema) customize("id", widget=deform.widget.HiddenWidget(), missing=colander.drop) customize("task_id", missing=colander.required) customize("description", validator=forms.textarea_node_validator) customize("amount", typ=AmountType(5), missing=colander.required) return schema
class PaymentSchema(colander.MappingSchema): """ colander schema for payment recording """ come_from = forms.come_from_node() bank_remittance_id = colander.SchemaNode( colander.String(), title=u"Identifiant de la remise en banque", description=u"Ce champ est un indicateur permettant de \ retrouver la remise en banque à laquelle cet encaissement est associé", default=deferred_bank_remittance_id_default, ) amount = colander.SchemaNode( AmountType(5), title=u"Montant de l'encaissement", validator=deferred_total_validator, default=deferred_amount_default, ) date = forms.today_node() mode = colander.SchemaNode( colander.String(), title=u"Mode de paiement", widget=deferred_payment_mode_widget, validator=deferred_payment_mode_validator, ) bank_id = colander.SchemaNode( colander.Integer(), title=u"Banque", missing=colander.drop, widget=deferred_bank_widget, validator=deferred_bank_validator, default=forms.get_deferred_default(BankAccount), ) tva_id = colander.SchemaNode(colander.Integer(), title=u"Tva liée à cet encaissement", widget=forms.get_deferred_select( Tva, mandatory=True, keys=('id', 'name')), validator=deferred_tva_id_validator) resulted = colander.SchemaNode( colander.Boolean(), title=u"Soldé", description="""Indique que le document est soldé ( ne recevra plus de paiement), si le montant indiqué correspond au montant de la facture celle-ci est soldée automatiquement""", default=False, )
class MultiplePaymentSchema(colander.MappingSchema): """ colander schema for payment recording """ come_from = forms.come_from_node() bank_remittance_id = colander.SchemaNode( colander.String(), title=u"Identifiant de la remise en banque", default= deferred_remittance_amount_default, # FIXME: C'est quoi cette ligne ? ) payment_amount = colander.SchemaNode( AmountType(5), title=u"Montant du paiement", description=u"Ce champ permet de contrôler que la somme des \ encaissements saisis dans ce formulaire correspondent bien au montant du \ paiement.", validator=deferred_total_validator, default=deferred_amount_default, ) date = forms.today_node(title=u"Date de la remise") mode = colander.SchemaNode( colander.String(), title=u"Mode de paiement", widget=deferred_payment_mode_widget, validator=deferred_payment_mode_validator, ) bank_id = colander.SchemaNode( colander.Integer(), title=u"Banque", missing=colander.drop, widget=deferred_bank_widget, default=forms.get_deferred_default(BankAccount), ) tvas = TvaPaymentSequence(title=u'Encaissements par taux de Tva') resulted = colander.SchemaNode( colander.Boolean(), title=u"Soldé", description="""Indique que le document est soldé ( ne recevra plus de paiement), si le montant indiqué correspond au montant de la facture celle-ci est soldée automatiquement""", default=False, )
def _customize_task_fields(schema): """ Add Field customization to the task form schema :param obj schema: The schema to modify """ schema.after_bind = task_after_bind customize = functools.partial(forms.customize_field, schema) customize("id", widget=deform.widget.HiddenWidget(), missing=colander.drop) customize( "status", widget=deform.widget.SelectWidget(values=zip(ALL_STATES, ALL_STATES)), validator=colander.OneOf(ALL_STATES), ) customize( "status_comment", widget=deform.widget.TextAreaWidget(), ) customize( "status_person_id", widget=get_deferred_user_choice(), ) customize( "description", widget=deform.widget.TextAreaWidget(), validator=forms.textarea_node_validator, missing=colander.required, ) customize("date", missing=colander.required) for field_name in "ht", "ttc", "tva", "expenses_ht": customize(field_name, typ=AmountType(5)) customize( "address", widget=deform.widget.TextAreaWidget(), validator=forms.textarea_node_validator, missing=colander.required, ) customize( "workplace", widget=deform.widget.TextAreaWidget(), ) customize( "payment_conditions", widget=deform.widget.TextAreaWidget(), validator=forms.textarea_node_validator, missing=colander.required, ) customize( "mentions", children=forms.get_sequence_child_item(TaskMention), ) customize( "line_groups", validator=colander.Length(min=1, min_err=u"Une entrée est requise"), missing=colander.required, ) if 'line_groups' in schema: child_schema = schema['line_groups'].children[0] _customize_tasklinegroup_fields(child_schema) if "discount_lines" in schema: child_schema = schema['discount_lines'].children[0] _customize_discountline_fields(child_schema) return schema
class Payment(DBBASE, PersistentACLMixin): """ Payment entry """ __tablename__ = 'payment' __table_args__ = default_table_args id = Column(Integer, primary_key=True) created_at = Column( DateTime(), info={'colanderalchemy': { 'exclude': True, 'title': u"Créé(e) le", }}, default=datetime.datetime.now, ) updated_at = Column(DateTime(), info={ 'colanderalchemy': { 'exclude': True, 'title': u"Mis(e) à jour le", } }, default=datetime.datetime.now, onupdate=datetime.datetime.now) mode = Column(String(50), info={ 'colanderalchemy': { 'title': u"Mode de paiement", 'validator': forms.get_deferred_select_validator(PaymentMode, id_key='label'), 'missing': colander.required, } }) amount = Column( BigInteger(), info={ 'colanderalchemy': { "title": u"Montant", 'missing': colander.required, "typ": AmountType(5) } }, ) remittance_amount = Column( String(255), info={ 'colanderalchemy': { 'title': u"Identifiant de remise en banque", 'missing': colander.required, } }, ) date = Column( DateTime(), info={ 'colanderalchemy': { 'title': u"Date de remise", 'missing': colander.required, } }, default=datetime.datetime.now, ) exported = Column(Boolean(), default=False) task_id = Column(Integer, ForeignKey('task.id', ondelete="cascade"), info={ 'colanderalchemy': { 'title': u"Identifiant du document", 'missing': colander.required, } }) bank_id = Column(ForeignKey('bank_account.id'), info={ 'colanderalchemy': { 'title': u"Compte en banque", 'missing': colander.required, 'validator': forms.get_deferred_select_validator(BankAccount), } }) tva_id = Column(ForeignKey('tva.id'), info={ 'colanderalchemy': { 'title': u"Tva associée à ce paiement", 'validator': forms.get_deferred_select_validator(Tva), 'missing': colander.drop, } }, nullable=True) user_id = Column( ForeignKey('accounts.id'), info={ 'colanderalchemy': { 'title': u"Utilisateur", 'missing': colander.required, } }, ) user = relationship( "User", info={'colanderalchemy': { 'exclude': True }}, ) bank = relationship("BankAccount", back_populates='payments', info={'colanderalchemy': { 'exclude': True }}) tva = relationship("Tva", info={'colanderalchemy': {'exclude': True}}) task = relationship( "Task", primaryjoin="Task.id==Payment.task_id", ) # Formatting precision precision = 5 # Usefull aliases @property def invoice(self): return self.task @property def parent(self): return self.task # Simple function def get_amount(self): return self.amount def __unicode__(self): return u"<Payment id:{s.id} task_id:{s.task_id} amount:{s.amount}\ mode:{s.mode} date:{s.date}".format(s=self)
class DiscountLine(DBBASE, DiscountLineCompute): """ A discount line """ __tablename__ = 'discount' __table_args__ = default_table_args id = Column( Integer, primary_key=True, nullable=False, info={'colanderalchemy': { 'widget': deform.widget.HiddenWidget() }}) task_id = Column(Integer, ForeignKey( 'task.id', ondelete="cascade", ), info={ 'colanderalchemy': { 'title': u"Identifiant du document", 'missing': colander.required, } }) description = Column(Text, info={ 'colanderalchemy': { 'widget': deform.widget.TextAreaWidget(), 'validator': forms.textarea_node_validator, } }) amount = Column( BigInteger(), info={ 'colanderalchemy': { 'typ': AmountType(5), 'title': 'Montant', 'missing': colander.required, } }, ) tva = Column(Integer, nullable=False, default=196, info={ "colanderalchemy": { "typ": AmountType(2), "validator": forms.get_deferred_select_validator(Tva, id_key='value'), "missing": colander.required } }) task = relationship( "Task", uselist=False, info={'colanderalchemy': forms.EXCLUDED}, ) def __json__(self, request): return dict( id=self.id, task_id=self.task_id, description=self.description, amount=integer_to_amount(self.amount, 5), tva=integer_to_amount(self.tva, 2), ) def duplicate(self): """ return the equivalent InvoiceLine """ line = DiscountLine() line.tva = self.tva line.amount = self.amount line.description = self.description return line def __repr__(self): return u"<DiscountLine amount : {s.amount} tva:{s.tva} id:{s.id}>"\ .format(s=self)
class Task(Node): """ Metadata pour une tâche (estimation, invoice) """ __tablename__ = 'task' __table_args__ = default_table_args __mapper_args__ = {'polymorphic_identity': 'task'} _autonomie_service = TaskService __colanderalchemy_config__ = { 'after_bind': task_after_bind, } id = Column( Integer, ForeignKey('node.id'), info={ 'colanderalchemy': { 'exclude': deform.widget.HiddenWidget() }, 'export': forms.EXCLUDED }, primary_key=True, ) phase_id = Column( ForeignKey('phase.id'), info={ 'colanderalchemy': forms.EXCLUDED, "export": forms.EXCLUDED, }, ) status = Column( String(10), info={ 'colanderalchemy': { 'title': u"Statut", 'widget': deform.widget.SelectWidget(values=zip(ALL_STATES, ALL_STATES)), "validator": colander.OneOf(ALL_STATES), }, 'export': forms.EXCLUDED }) status_comment = Column( Text, info={ "colanderalchemy": { "title": u"Commentaires", 'widget': deform.widget.TextAreaWidget() }, 'export': forms.EXCLUDED }, default="", ) status_person_id = Column( ForeignKey('accounts.id'), info={ 'colanderalchemy': { "title": u"Dernier utilisateur à avoir modifié le document", 'widget': get_deferred_user_choice() }, "export": forms.EXCLUDED, }, ) status_date = Column(Date(), default=datetime.date.today, info={ 'colanderalchemy': { "title": u"Date du dernier changement de statut", }, 'export': forms.EXCLUDED }) date = Column(Date(), info={ "colanderalchemy": { "title": u"Date du document", "missing": colander.required } }, default=datetime.date.today) owner_id = Column( ForeignKey('accounts.id'), info={ 'colanderalchemy': forms.EXCLUDED, "export": forms.EXCLUDED, }, ) description = Column( Text, info={ 'colanderalchemy': { "title": u"Objet", 'widget': deform.widget.TextAreaWidget(), 'validator': forms.textarea_node_validator, 'missing': colander.required, } }, ) ht = Column(BigInteger(), info={ 'colanderalchemy': { "title": u"Montant HT (cache)", "typ": AmountType(5), }, 'export': forms.EXCLUDED, }, default=0) tva = Column(BigInteger(), info={ 'colanderalchemy': { "title": u"Montant TVA (cache)", "typ": AmountType(5), }, 'export': forms.EXCLUDED, }, default=0) ttc = Column(BigInteger(), info={ 'colanderalchemy': { "title": u"Montant TTC (cache)", "typ": AmountType(5), }, 'export': forms.EXCLUDED, }, default=0) company_id = Column( Integer, ForeignKey('company.id'), info={ 'colanderalchemy': forms.EXCLUDED, 'export': forms.EXCLUDED, }, ) project_id = Column( Integer, ForeignKey('project.id'), info={ 'colanderalchemy': forms.EXCLUDED, 'export': forms.EXCLUDED, }, ) customer_id = Column( Integer, ForeignKey('customer.id'), info={ 'colanderalchemy': forms.EXCLUDED, 'export': forms.EXCLUDED, }, ) project_index = deferred( Column( Integer, info={ 'colanderalchemy': { "title": u"Index dans le projet", }, 'export': forms.EXCLUDED, }, ), group='edit', ) company_index = deferred( Column( Integer, info={ 'colanderalchemy': { "title": u"Index du document à l'échelle de l'entreprise", }, 'export': forms.EXCLUDED, }, ), group='edit', ) official_number = Column( Integer, info={ 'colanderalchemy': { "title": u"Identifiant du document (facture/avoir)", }, 'export': { 'label': u"Numéro de facture" }, }, default=None, ) internal_number = deferred(Column( String(255), default=None, info={ 'colanderalchemy': { "title": u"Identifiant du document dans la CAE", }, 'export': forms.EXCLUDED, }), group='edit') display_units = deferred(Column(Integer, info={ 'colanderalchemy': { "title": u"Afficher le détail ?", "validator": colander.OneOf((0, 1)) }, 'export': forms.EXCLUDED, }, default=0), group='edit') # Not used in latest invoices expenses = deferred(Column(BigInteger(), info={ 'colanderalchemy': forms.EXCLUDED, 'export': forms.EXCLUDED, }, default=0), group='edit') expenses_ht = deferred( Column(BigInteger(), info={ 'colanderalchemy': { 'typ': AmountType(5), 'title': u'Frais', }, 'export': forms.EXCLUDED, }, default=0), group='edit', ) address = deferred( Column( Text, default="", info={ 'colanderalchemy': { 'title': u'Adresse', 'widget': deform.widget.TextAreaWidget(), 'validator': forms.textarea_node_validator, 'missing': colander.required, }, 'export': forms.EXCLUDED, }, ), group='edit', ) workplace = deferred( Column(Text, default='', info={ 'colanderalchemy': { 'title': u"Lieu d'éxécution des travaux", 'widget': deform.widget.TextAreaWidget(), } })) payment_conditions = deferred( Column( Text, info={ 'colanderalchemy': { "title": u"Conditions de paiement", 'widget': deform.widget.TextAreaWidget(), 'validator': forms.textarea_node_validator, 'missing': colander.required, }, 'export': forms.EXCLUDED, }, ), group='edit', ) round_floor = deferred( Column(Boolean(), default=False, info={ 'colanderalchemy': { 'exlude': True, 'title': u"Méthode d'arrondi 'à l'ancienne' ? (floor)" }, 'export': forms.EXCLUDED, }), group='edit', ) prefix = Column(String(15), default='', info={ "colanderalchemy": { 'title': u"Préfixe du numéro de facture", }, 'export': forms.EXCLUDED, }) # Organisationnal Relationships status_person = relationship( "User", primaryjoin="Task.status_person_id==User.id", backref=backref( "taskStatuses", info={ 'colanderalchemy': forms.EXCLUDED, 'export': forms.EXCLUDED, }, ), info={ 'colanderalchemy': forms.EXCLUDED, 'export': forms.EXCLUDED, }, ) owner = relationship( "User", primaryjoin="Task.owner_id==User.id", backref=backref( "ownedTasks", info={ 'colanderalchemy': forms.EXCLUDED, 'export': forms.EXCLUDED, }, ), info={ 'colanderalchemy': forms.EXCLUDED, 'export': forms.EXCLUDED, }, ) phase = relationship( "Phase", primaryjoin="Task.phase_id==Phase.id", backref=backref( "tasks", order_by='Task.date', info={ 'colanderalchemy': forms.EXCLUDED, 'export': forms.EXCLUDED, }, ), info={ 'colanderalchemy': forms.EXCLUDED, 'export': forms.EXCLUDED, }, ) company = relationship( "Company", primaryjoin="Task.company_id==Company.id", info={ 'colanderalchemy': forms.EXCLUDED, 'export': { 'related_key': "name", "label": "Entreprise" }, }, ) project = relationship( "Project", primaryjoin="Task.project_id==Project.id", info={ 'colanderalchemy': forms.EXCLUDED, 'export': forms.EXCLUDED, }, ) customer = relationship( "Customer", primaryjoin="Customer.id==Task.customer_id", backref=backref( 'tasks', order_by='Task.date', info={ 'colanderalchemy': forms.EXCLUDED, "export": forms.EXCLUDED, }, ), info={ 'colanderalchemy': forms.EXCLUDED, 'export': { 'related_key': 'label', 'label': u"Client" }, }, ) # Content relationships discounts = relationship( "DiscountLine", info={ 'colanderalchemy': { 'title': u"Remises" }, 'export': forms.EXCLUDED, }, order_by='DiscountLine.tva', cascade="all, delete-orphan", back_populates='task', ) payments = relationship( "Payment", primaryjoin="Task.id==Payment.task_id", info={ 'colanderalchemy': forms.EXCLUDED, 'export': forms.EXCLUDED, }, order_by='Payment.date', cascade="all, delete-orphan", back_populates='task', ) mentions = relationship( "TaskMention", secondary=TASK_MENTION, order_by="TaskMention.order", back_populates="tasks", info={ 'colanderalchemy': { 'children': forms.get_sequence_child_item(TaskMention), }, 'export': forms.EXCLUDED, }, ) line_groups = relationship( "TaskLineGroup", order_by='TaskLineGroup.order', cascade="all, delete-orphan", collection_class=ordering_list('order'), info={ 'colanderalchemy': { 'title': u"Unités d'oeuvre", "validator": colander.Length(min=1, min_err=u"Une entrée est requise"), "missing": colander.required }, 'export': forms.EXCLUDED, }, primaryjoin="TaskLineGroup.task_id==Task.id", back_populates='task', ) statuses = relationship( "TaskStatus", order_by="desc(TaskStatus.status_date), desc(TaskStatus.id)", cascade="all, delete-orphan", back_populates='task', info={ 'colanderalchemy': forms.EXCLUDED, 'export': forms.EXCLUDED, }) _name_tmpl = u"Task {}" _number_tmpl = u"{s.project.code}_{s.customer.code}_T{s.project_index}\ _{s.date:%m%y}" state_manager = None def __init__(self, company, customer, project, phase, user): company_index = self._get_company_index(company) project_index = self._get_project_index(project) self.status = 'draft' self.company = company self.customer = customer self.address = customer.full_address self.project = project self.phase = phase self.owner = user self.status_person = user self.date = datetime.date.today() self.set_numbers(company_index, project_index) # We add a default task line group self.line_groups.append(TaskLineGroup(order=0)) def _get_project_index(self, project): """ Return the index of the current object in the associated project :param obj project: A Project instance in which we will look to get the current doc index :returns: The next number :rtype: int """ return -1 def _get_company_index(self, company): """ Return the index of the current object in the associated company :param obj company: A Company instance in which we will look to get the current doc index :returns: The next number :rtype: int """ return -1 def set_numbers(self, company_index, project_index): """ Handle all attributes related to the given number :param int company_index: The index of the task in the company :param int project_index: The index of the task in its project """ if company_index is None or project_index is None: raise Exception("Indexes should not be None") self.company_index = company_index self.project_index = project_index self.internal_number = self._number_tmpl.format(s=self) self.name = self._name_tmpl.format(project_index) @property def default_line_group(self): return self.line_groups[0] def __json__(self, request): """ Return the datas used by the json renderer to represent this task """ return dict( id=self.id, name=self.name, created_at=self.created_at.isoformat(), updated_at=self.updated_at.isoformat(), phase_id=self.phase_id, status=self.status, status_comment=self.status_comment, status_person_id=self.status_person_id, # status_date=self.status_date.isoformat(), date=self.date.isoformat(), owner_id=self.owner_id, description=self.description, ht=integer_to_amount(self.ht, 5), tva=integer_to_amount(self.tva, 5), ttc=integer_to_amount(self.ttc, 5), company_id=self.company_id, project_id=self.project_id, customer_id=self.customer_id, project_index=self.project_index, company_index=self.company_index, official_number=self.official_number, internal_number=self.internal_number, display_units=self.display_units, expenses_ht=integer_to_amount(self.expenses_ht, 5), address=self.address, workplace=self.workplace, payment_conditions=self.payment_conditions, prefix=self.prefix, status_history=[ status.__json__(request) for status in self.statuses ], discounts=[ discount.__json__(request) for discount in self.discounts ], payments=[payment.__json__(request) for payment in self.payments], mentions=[mention.id for mention in self.mentions], line_groups=[ group.__json__(request) for group in self.line_groups ], attachments=[ f.__json__(request) for f in self.children if f.type_ == 'file' ]) def set_status(self, status, request, **kw): """ set the status of a task through the state machine """ return self.state_manager.process(status, self, request, **kw) def check_status_allowed(self, status, request, **kw): return self.state_manager.check_allowed(status, self, request) @validates('status') def change_status(self, key, status): """ fired on status change, stores a new taskstatus for each status change """ log.debug(u"# Task status change #") actual_status = self.status log.debug(u" + was {0}, becomes {1}".format(actual_status, status)) return status def get_company(self): """ Return the company owning this task """ return self.company def get_customer(self): """ Return the customer of the current task """ return self.customer def get_company_id(self): """ Return the id of the company owning this task """ return self.company.id def __repr__(self): return u"<Task status:{s.status} id:{s.id}>".format(s=self) def get_groups(self): return [group for group in self.line_groups if group.lines] @property def all_lines(self): """ Returns a list with all task lines of the current task """ result = [] for group in self.line_groups: result.extend(group.lines) return result def get_tva_objects(self): return self._autonomie_service.get_tva_objects(self) @classmethod def get_valid_tasks(cls, *args): return cls._autonomie_service.get_valid_tasks(cls, *args) @classmethod def get_waiting_estimations(cls, *args): return cls._autonomie_service.get_waiting_estimations(*args) @classmethod def get_waiting_invoices(cls, *args): return cls._autonomie_service.get_waiting_invoices(cls, *args)
class PaymentLine(DBBASE): """ payments lines """ __tablename__ = 'estimation_payment' __table_args__ = default_table_args id = Column( Integer, primary_key=True, nullable=False, info={'colanderalchemy': { 'widget': deform.widget.HiddenWidget() }}) task_id = Column( Integer, ForeignKey('estimation.id', ondelete="cascade"), info={ 'colanderalchemy': { 'title': u"Identifiant du document", 'missing': colander.required, } }, ) order = Column(Integer, info={'colanderalchemy': { 'title': u"Ordre" }}, default=1) description = Column( Text, info={ 'colanderalchemy': { 'title': u"Description", 'validator': forms.textarea_node_validator, } }, ) amount = Column( BigInteger(), info={ 'colanderalchemy': { 'title': u"Montant", 'typ': AmountType(5), 'missing': colander.required, } }, ) date = Column(Date(), info={'colanderalchemy': { "title": u"Date", }}, default=datetime.date.today) task = relationship( "Estimation", info={'colanderalchemy': { 'exclude': True }}, ) def duplicate(self): """ duplicate a paymentline """ return PaymentLine( order=self.order, amount=self.amount, description=self.description, date=datetime.date.today(), ) def __repr__(self): return u"<PaymentLine id:{s.id} task_id:{s.task_id} amount:{s.amount}\ date:{s.date}".format(s=self) def __json__(self, request): return dict( id=self.id, order=self.order, index=self.order, description=self.description, cost=integer_to_amount(self.amount, 5), amount=integer_to_amount(self.amount, 5), date=self.date.isoformat(), task_id=self.task_id, )
def test_amount_type(): a = AmountType() assert a.serialize(None, 15000) == "150.0" assert a.deserialize(None, u"79.4") == 7940 assert a.deserialize(None, u"292,65") == 29265
class ExpenseKmLine(BaseExpenseLine, ExpenseKmLineCompute): """ Model representing a specific expense related to kilometric fees :param start: starting point :param end: endpoint :param km: Number of kilometers """ __tablename__ = "expensekm_line" __table_args__ = default_table_args __mapper_args__ = dict(polymorphic_identity='expensekmline') id = Column(Integer, ForeignKey('baseexpense_line.id'), primary_key=True, info={'colanderalchemy': forms.EXCLUDED}) start = Column(String(150), default="", info={"colanderalchemy": { "title": u"Point de départ" }}) end = Column(String(150), default="", info={"colanderalchemy": { "title": u"Point d'arrivée" }}) km = Column( Integer, info={ 'colanderalchemy': { 'typ': AmountType(2), 'title': 'Nombre de kilomètres', 'missing': colander.required, } }, ) sheet = relationship("ExpenseSheet", uselist=False, info={'colanderalchemy': forms.EXCLUDED}) def __json__(self, request): res = BaseExpenseLine.__json__(self, request) res.update( dict( km=integer_to_amount(self.km), start=self.start, end=self.end, vehicle=self.vehicle, )) return res @property def vehicle(self): return self.type_object.label def duplicate(self, sheet): line = ExpenseKmLine() line.description = self.description line.category = self.category line.type_object = self.type_object.get_by_year(sheet.year) line.start = self.start line.end = self.end line.km = self.km return line
class Tva(DBBASE): """ `id` int(2) NOT NULL auto_increment, `name` varchar(8) NOT NULL, `value` int(5) `default` int(2) default 0 #rajouté par mise à jour 1.2 """ __colanderalchemy_config__ = { "title": u"un taux de TVA", "validation_msg": u"Les taux de Tva ont bien été configurés", "help_msg": u"""Configurez les taux de Tva disponibles utilisés dans \ Autonomie, ainsi que les produits associés.<br /> \ Une Tva est composée :<ul><li>D'un libellé (ex : TVA 20%)</li> \ <li>D'un montant (ex : 20)</li> \ <li>D'un ensemble d'informations comptables</li> \ <li>D'un ensemble de produits associés</li> \ <li> D'une mention : si elle est renseignée, celle-ci viendra se placer en lieu et place du libellé (ex : Tva non applicable en vertu ...) </ul><br /> \ <b>Note : les montants doivent tous être distincts, si vous utilisez \ plusieurs Tva à 0%, utilisez des montants négatifs pour les \ différencier. \ """, 'widget': deform_extensions.GridFormWidget(named_grid=TVA_GRID) } __tablename__ = 'tva' __table_args__ = default_table_args id = Column( 'id', Integer, primary_key=True, info={'colanderalchemy': { 'widget': deform.widget.HiddenWidget() }}, ) active = Column( Boolean(), default=True, info={'colanderalchemy': { 'exclude': True }}, ) name = Column( "name", String(15), nullable=False, info={'colanderalchemy': { 'title': u'Libellé du taux de TVA', }}, ) value = Column( "value", Integer, info={ "colanderalchemy": { 'title': u'Valeur', 'typ': AmountType(), 'description': u"Le pourcentage associé (ex : 19.6)", } }, ) compte_cg = Column( "compte_cg", String(125), default="", info={'colanderalchemy': dict(title=u"Compte CG de Tva")}) code = Column("code", String(125), default="", info={'colanderalchemy': dict(title=u"Code de Tva")}) compte_a_payer = Column( String(125), default='', info={ 'colanderalchemy': dict( title=u"Compte de Tva à payer", description=u"Utilisé dans les exports comptables des \ encaissements", ) }) mention = Column(Text, info={ 'colanderalchemy': { 'title': u"Mentions spécifiques à cette TVA", 'description': u"""Si cette Tva est utilisée dans un devis/une facture,la mention apparaitra dans la sortie PDF (ex: Mention pour la tva liée aux formations ...)""", 'widget': deform.widget.TextAreaWidget(rows=1), 'preparer': clean_html, 'missing': colander.drop, } }) default = Column( "default", Boolean(), info={ "colanderalchemy": { 'title': u'Cette tva doit-elle être proposée par défaut ?' } }, ) products = relationship( "Product", cascade="all, delete-orphan", info={ 'colanderalchemy': { 'title': u"Comptes produit associés", "widget": deform.widget.SequenceWidget( add_subitem_text_template=u"Ajouter un compte produit", ) }, }, back_populates='tva', ) @classmethod def query(cls, include_inactive=False): q = super(Tva, cls).query() if not include_inactive: q = q.filter(Tva.active == True) return q.order_by('value') @classmethod def by_value(cls, value): """ Returns the Tva matching this value """ return super(Tva, cls).query().filter(cls.value == value).one() @classmethod def get_default(cls): return cls.query().filter_by(default=True).first() def __json__(self, request): return dict( id=self.id, value=integer_to_amount(self.value, 2), label=self.name, name=self.name, default=self.default, products=[product.__json__(request) for product in self.products], ) @classmethod def unique_value(cls, value, tva_id=None): """ Check that the given value has not already been attributed to a tva entry :param int value: The value currently configured :param int tva_id: The optionnal id of the current tva object (edition mode) :returns: True/False :rtype: bool """ query = cls.query(include_inactive=True) if tva_id: query = query.filter(not_(cls.id == tva_id)) return query.filter_by(value=value).count() == 0
class TaskLine(DBBASE, LineCompute): """ Estimation/Invoice/CancelInvoice lines """ __colanderalchemy_config__ = { 'validator': tva_product_validator, 'after_bind': taskline_after_bind, } __table_args__ = default_table_args id = Column( Integer, primary_key=True, info={'colanderalchemy': { 'widget': deform.widget.HiddenWidget() }}) group_id = Column(Integer, ForeignKey('task_line_group.id', ondelete="cascade"), info={'colanderalchemy': forms.EXCLUDED}) order = Column( Integer, default=1, ) description = Column( Text, info={ 'colanderalchemy': { 'widget': deform.widget.RichTextWidget(options={ 'language': "fr_FR", 'content_css': "/fanstatic/fanstatic/css/richtext.css", }, ), 'validator': forms.textarea_node_validator, } }, ) cost = Column( BigInteger(), info={ 'colanderalchemy': { 'typ': AmountType(5), 'title': 'Montant', 'missing': colander.required, } }, default=0, ) quantity = Column(Float(), info={ 'colanderalchemy': { "title": u"Quantité", 'typ': QuantityType(), 'missing': colander.required, } }, default=1) unity = Column( String(100), info={ 'colanderalchemy': { 'title': u"Unité", 'validator': forms.get_deferred_select_validator( WorkUnit, id_key='label', ), 'missing': colander.drop, } }, ) tva = Column(Integer, info={ 'colanderalchemy': { 'typ': AmountType(2), 'title': 'Tva (en %)', 'validator': forms.get_deferred_select_validator(Tva, id_key='value'), 'missing': colander.required } }, nullable=False, default=2000) product_id = Column(Integer, info={ 'colanderalchemy': { 'validator': forms.get_deferred_select_validator(Product), 'missing': colander.drop, } }) group = relationship(TaskLineGroup, primaryjoin="TaskLine.group_id==TaskLineGroup.id", info={'colanderalchemy': forms.EXCLUDED}) product = relationship("Product", primaryjoin="Product.id==TaskLine.product_id", uselist=False, foreign_keys=product_id, info={'colanderalchemy': forms.EXCLUDED}) def duplicate(self): """ duplicate a line """ newone = TaskLine() newone.order = self.order newone.cost = self.cost newone.tva = self.tva newone.description = self.description newone.quantity = self.quantity newone.unity = self.unity newone.product_id = self.product_id return newone def gen_cancelinvoice_line(self): """ Return a cancel invoice line duplicating this one """ newone = TaskLine() newone.order = self.order newone.cost = -1 * self.cost newone.tva = self.tva newone.description = self.description newone.quantity = self.quantity newone.unity = self.unity newone.product_id = self.product_id return newone def __repr__(self): return u"<TaskLine id:{s.id} task_id:{s.group.task_id} cost:{s.cost} \ quantity:{s.quantity} tva:{s.tva}>".format(s=self) def __json__(self, request): result = dict( id=self.id, order=self.order, cost=integer_to_amount(self.cost, 5), tva=integer_to_amount(self.tva, 2), description=self.description, quantity=self.quantity, unity=self.unity, group_id=self.group_id, ) if self.product_id is not None: result['product_id'] = self.product_id return result @property def task(self): return self.group.task @classmethod def from_sale_product(cls, sale_product): """ Build an instance based on the given sale_product :param obj sale_product: A SaleProduct instance :returns: A TaskLine instance """ result = cls() result.description = sale_product.description result.cost = amount(sale_product.value, 5) result.tva = sale_product.tva result.unity = sale_product.unity result.quantity = 1 return result