Пример #1
0
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
Пример #2
0
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
Пример #3
0
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
Пример #4
0
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
Пример #5
0
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
Пример #6
0
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",
    )
Пример #7
0
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",
    )
Пример #8
0
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)
Пример #9
0
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
Пример #10
0
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,
    )
Пример #11
0
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,
    )
Пример #12
0
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
Пример #13
0
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)
Пример #14
0
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)
Пример #15
0
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)
Пример #16
0
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,
        )
Пример #17
0
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
Пример #18
0
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
Пример #19
0
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
Пример #20
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