class Payment(db.Model): __tablename__ = 'payment' id = db.Column(db.BigInteger, primary_key=True, nullable=False) invoice_id = db.Column(db.BigInteger, db.ForeignKey('invoice.id')) date = db.Column(db.DateTime, default=datetime.now, nullable=False) currency_code = db.Column(db.Unicode(3), nullable=False) method = db.Column(db.Unicode(50), nullable=False) description = db.Column(db.Unicode(255), nullable=True) amount = db.Column(db.Numeric(8, 2), default=0, nullable=False) created_at = db.Column(db.DateTime, default=datetime.now, nullable=False) #relation definitions invoice = db.relation('Invoice', primaryjoin='Payment.invoice_id==Invoice.id', backref=db.backref('payments', lazy='dyanmic'))
class StripePayment(db.Model): __tablename__ = 'stripe_payment' id = db.Column(db.Integer(11), primary_key=True, nullable=False) user_id = db.Column(db.Integer(11), db.ForeignKey('user.id'), nullable=False) payment_id = db.Column(db.Integer(11), db.ForeignKey('payment.id'), nullable=True) invoice_id = db.Column(db.Integer(11), db.ForeignKey('invoice.id'), nullable=False) amount = db.Column(db.Numeric(8, 2), default=0) token = db.Column(db.Unicode(100), nullable=False) state = db.Column(db.Unicode(20), default=u'initialized') error_message = db.Column(db.Unicode(255), nullable=True) charge_id = db.Column(db.Unicode(100), nullable=True) charge = db.Column(db.UnicodeText(4294967295), nullable=False, default=u'{}') created_at = db.Column(db.DateTime(), nullable=False, default=datetime.now) @classmethod def get_or_create_for_user(cls, user_id): account = cls.query.filter_by(user_id=user_id).first() if not account: model = cls() model.user_id = user_id db.session.add(model) db.session.commit() return account def create_payment_object(self): """Create related payment object""" payment = Payment() payment.invoice_id = self.invoice_id payment.date = datetime.now() payment.currency_code = self.invoice.currency_code payment.amount = self.amount payment.method = 'stripe' payment.description = 'Credit card payment' db.session.add(payment) db.session.commit() return payment
class Transaction(db.Model): __tablename__ = 'transaction' id = db.Column(db.Integer(11), primary_key=True, nullable=False) user_id = db.Column(db.Integer(11), db.ForeignKey('user.id'), nullable=False) subscription_id = db.Column(db.Integer(11), db.ForeignKey('subscription.id'), nullable=False) success = db.Column(db.Boolean, default=False) amount = db.Column(db.Numeric(8, 2), default=0) charge_id = db.Column(db.Unicode(100), nullable=True) charge = db.Column(db.UnicodeText(4294967295), nullable=True, default=u'{}') updated_at = db.Column(db.DateTime, nullable=False, default=datetime.now) created_at = db.Column(db.DateTime, nullable=False, default=datetime.now, onupdate=datetime.now)
class TaxRate(db.Model): __tablename__ = 'tax_rate' id = db.Column(u'id', db.BigInteger, primary_key=True, nullable=False) user_id = db.Column(u'user_id', db.BigInteger, db.ForeignKey('user.id')) type = db.Column(u'type', db.String(255)) name = db.Column(u'name', db.String(255)) rate = db.Column(u'rate', db.Numeric(8, 2), nullable=True) updated_at = db.Column(u'updated_at', db.DateTime(), nullable=False, default=get_current_time()) created_at = db.Column(u'created_at', db.DateTime(), nullable=False, default=get_current_time()) user = db.relation('User', lazy='select', backref='tax_rates') def serialize(self): return model_to_dict(self)
class Plan(db.Model): __tablename__ = 'plan' id = db.Column(db.Integer(11), primary_key=True, nullable=False) name = db.Column(db.Unicode(20)) gateway_uid = db.Column(db.Unicode(255)) description = db.Column(db.Unicode(255)) amount = db.Column(db.Numeric(8, 2), default=0) billing_interval = db.Column(db.Enum('daily', 'weekly', 'monthly', 'yearly'), default='monthly') created_at = db.Column(db.DateTime, nullable=False, default=datetime.now) # relationships subscriptions = db.relation('Subscription', backref=db.backref('plan', lazy='joined', uselist=False)) @property def interval_name(self): if self.billing_interval == 'monthly': return 'month'
class GoCardlessPayment(db.Model): __tablename__ = 'gocardless_payment' id = db.Column(db.Integer(11), primary_key=True, nullable=False) invoice_id = db.Column(db.Integer(11), db.ForeignKey('invoice.id'), nullable=False) user_id = db.Column(db.Integer(11), db.ForeignKey('user.id'), nullable=False) payment_id = db.Column(db.Integer(11), db.ForeignKey('payment.id'), nullable=True) amount = db.Column(db.Numeric(8, 2), default=0) reference = db.Column(db.Unicode(100), nullable=False) state = db.Column(db.Unicode(20), default=u'initialized') resource_id = db.Column(db.Unicode(100), nullable=True) resource_uri = db.Column(db.Unicode(255), nullable=True) error_message = db.Column(db.Unicode(255), nullable=True) created_at = db.Column(db.DateTime(), nullable=False, default=datetime.now) def create_payment_object(self): """Create related payment object""" payment = Payment() payment.invoice_id = self.invoice_id payment.date = datetime.now() payment.currency_code = self.invoice.currency_code payment.amount = self.amount payment.method = 'gocardless' payment.description = 'Direct debit payment' db.session.add(payment) db.session.commit() return payment
class InvoiceItem(db.Model): __tablename__ = 'invoice_item' id = db.Column(u'id', db.BigInteger, primary_key=True, nullable=False) invoice_id = db.Column(u'invoice_id', db.BigInteger, db.ForeignKey('invoice.id')) type_id = db.Column(u'type_id', db.BigInteger, db.ForeignKey('invoice_item_type.id')) tax_rate_id = db.Column(u'tax_rate_id', db.BigInteger, db.ForeignKey('tax_rate.id')) description = db.Column(u'description', db.String) quantity = db.Column(u'quantity', db.Numeric(8, 2)) price = db.Column(u'price', db.Numeric(8, 2)) tax = db.Column(u'tax', db.Numeric(8, 2)) total = db.Column(u'total', db.Numeric(8, 2)) sort_order = db.Column(u'sort_order', db.Integer, default=0) #relation definitions invoice_item_type = db.relation( 'InvoiceItemType', primaryjoin='InvoiceItem.type_id==InvoiceItemType.id') invoice = db.relation('Invoice', primaryjoin='InvoiceItem.invoice_id==Invoice.id', backref=db.backref('invoice_items', lazy='dyanmic')) tax_rate = db.relation('TaxRate', primaryjoin='InvoiceItem.tax_rate_id==TaxRate.id') def should_render_field(self, name): """Returns True if the field should be rendered in the item list on the invoice""" no_render = { 'Comment': ['quantity', 'price', 'tax', 'total'], 'VAT': ['quantity'] } type_name = self.invoice_item_type.name if not type_name in no_render: return True if name in no_render[type_name]: return False return True def quantity_str(self): """Returns a more sanely formatted quantity string, taking into account the type of the item that we're rendering""" if self.invoice_item_type.name == 'Hour': mins = self.quantity * 60 hours = 0 while mins >= 60: mins -= 60 hours += 1 if mins == 0: return hours else: mins = int(round(mins)) mins = str(mins).zfill(2) return '%s:%s' % (hours, mins) return int(self.quantity) if math.fmod(self.quantity, 1) == 0 else self.quantity def update_totals(self): """Recalculate the tax and the totals for this invoice""" if self.tax_rate_id > 0: rate = 0 if self.id: rate = self.tax_rate.rate else: from nano.models import Invoice, TaxRate rate = TaxRate.query.get(self.tax_rate_id).rate self.tax = self.quantity * float(self.price) * float(rate) / 100 else: self.tax = 0 self.total = self.quantity * float(self.price) return self.tax, self.total def serialize(self): """Serialize the invoice structure so that it can be used for JSON""" d = model_to_dict(self) d['InvoiceItemType'] = self.invoice_item_type.serialize() if self.tax_rate: d['TaxRate'] = self.tax_rate.serialize() return d
class Invoice(db.Model): __tablename__ = 'invoice' DATE_FORMAT = '%a %b %d %Y' id = db.Column(u'id', db.Integer, primary_key=True, nullable=False) user_id = db.Column(u'user_id', db.Integer, db.ForeignKey('user.id')) contact_id = db.Column(u'contact_id', db.Integer, db.ForeignKey('contact.id')) payment_term_id = db.Column(u'payment_term_id', db.Integer, db.ForeignKey('payment_term.id')) status = db.Column(u'status', db.String(255)) reference = db.Column(u'reference', db.String(255)) po_reference = db.Column(u'po_reference', db.String(255)) currency_code = db.Column(u'currency_code', db.String(5)) date_issued = db.Column(u'date_issued', db.DateTime) due_date = db.Column(u'due_date', db.DateTime) written_off_date = db.Column(u'written_off_date', db.DateTime) sub_total = db.Column(u'sub_total', db.Numeric(8, 2)) tax = db.Column(u'tax', db.Numeric(8, 2)) total = db.Column(u'total', db.Numeric(8, 2)) payment_status = db.Column(u'payment_status', db.Unicode(10), default=u'unpaid') updated_at = db.Column(u'updated_at', db.DateTime, nullable=False, default=get_current_time()) created_at = db.Column(u'created_at', db.DateTime, nullable=False, default=get_current_time()) # relations user = db.relation('User', primaryjoin='Invoice.user_id==User.id', backref='invoices') contact = db.relation('Contact', primaryjoin='Invoice.contact_id==Contact.id', backref='invoices') payment_term = db.relation('PaymentTerm', primaryjoin='Invoice.payment_term_id==PaymentTerm.id') invoice_link = db.relation('InvoiceLink', backref=db.backref('invoice', uselist=False, lazy='joined')) gocardless_payments = db.relation('GoCardlessPayment', backref=db.backref('invoice', uselist=False, lazy='joined')) stripe_payments = db.relation('StripePayment', backref=db.backref('invoice', uselist=False, lazy='joined')) @classmethod def next_invoice_number(cls, user): """Next the next invoice number for the user""" cur_max = cls.query.filter_by(user_id=user.id).count() cur_max += 1 return str(cur_max) @property def due_date_nice(self): return self.due_date.strftime(self.DATE_FORMAT) @property def date_issued_nice(self): return self.date_issued.strftime(self.DATE_FORMAT) def next_item_sort_order(self): """Generate the next number for the invoice item's sort order""" from nano.models import InvoiceItem total = InvoiceItem.query.filter_by(invoice_id=self.id).count() return total+1 def update_totals(self, commit=False): """Update total and tax""" sub_total = 0.0 tax = 0.0 for item in self.invoice_items: sub_total += float(item.total if item.total else 0) tax += float(item.tax if item.tax else 0) self.tax = tax self.sub_total = sub_total self.total = float(self.tax) + float(self.sub_total) if commit: db.session.add(self) db.session.commit() return True def serialize(self): d = model_to_dict(self) d['InvoiceItems'] = [item.serialize() for item in self.invoice_items] return d def update_payment_status(self): """Returns true if the amount has been paid""" payments = Payment.query.filter_by(invoice_id=self.id).all() total = 0.0 for payment in payments: total += float(payment.amount) if total >= self.total: self.payment_status = u'paid' else: self.payment_status = u'unpaid' db.session.add(self) db.session.commit() return False def get_status(self): if self.status == 'draft': return 'draft' if self.status == 'saved': paid = True if self.payment_status == 'paid' else False if paid: return 'paid' if self.due_date <= datetime.now(): return 'overdue' else: return 'saved' def __json__(self): return json_dumps(self.serialize())