push
This commit is contained in:
185
app/models/invoice.py
Normal file
185
app/models/invoice.py
Normal file
@@ -0,0 +1,185 @@
|
||||
import enum
|
||||
|
||||
from app.extensions import db
|
||||
from app.models.base import TimestampMixin
|
||||
|
||||
|
||||
invoice_tags = db.Table(
|
||||
'invoice_tags',
|
||||
db.Column('invoice_id', db.Integer, db.ForeignKey('invoice.id'), primary_key=True),
|
||||
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'), primary_key=True),
|
||||
)
|
||||
|
||||
|
||||
class InvoiceType(enum.Enum):
|
||||
PURCHASE = 'purchase'
|
||||
SALE = 'sale'
|
||||
CORRECTION = 'correction'
|
||||
|
||||
|
||||
class InvoiceStatus(enum.Enum):
|
||||
NEW = 'new'
|
||||
READ = 'read'
|
||||
ACCOUNTED = 'accounted'
|
||||
SENT = 'sent'
|
||||
ARCHIVED = 'archived'
|
||||
NEEDS_ATTENTION = 'needs_attention'
|
||||
ERROR = 'error'
|
||||
|
||||
|
||||
|
||||
INVOICE_TYPE_LABELS = {
|
||||
InvoiceType.PURCHASE: 'Zakupowa',
|
||||
InvoiceType.SALE: 'Sprzedażowa',
|
||||
InvoiceType.CORRECTION: 'Korekta',
|
||||
}
|
||||
|
||||
INVOICE_STATUS_LABELS = {
|
||||
InvoiceStatus.NEW: 'Nowa',
|
||||
InvoiceStatus.READ: 'Odczytana',
|
||||
InvoiceStatus.ACCOUNTED: 'Zaksięgowana',
|
||||
InvoiceStatus.SENT: 'Wysłana',
|
||||
InvoiceStatus.ARCHIVED: 'Archiwalna',
|
||||
InvoiceStatus.NEEDS_ATTENTION: 'Do księgowania',
|
||||
InvoiceStatus.ERROR: 'Błąd',
|
||||
}
|
||||
|
||||
ISSUED_STATUS_LABELS = {
|
||||
'draft': 'Robocza',
|
||||
'pending': 'Oczekuje wysyłki',
|
||||
'issued': 'Wysłana do KSeF',
|
||||
'received': 'Odebrana',
|
||||
'read': 'Odczytana',
|
||||
'accounted': 'Zaksięgowana',
|
||||
'queued': 'W kolejce',
|
||||
'error': 'Błąd',
|
||||
'sent': 'Wysłana',
|
||||
'needs_attention': 'Do księgowania',
|
||||
'issued_mock': 'Wysłana testowo',
|
||||
}
|
||||
|
||||
|
||||
class Invoice(TimestampMixin, db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
company_id = db.Column(db.Integer, db.ForeignKey('company.id'), index=True)
|
||||
ksef_number = db.Column(db.String(128), nullable=False, index=True)
|
||||
invoice_number = db.Column(db.String(128), nullable=False, index=True)
|
||||
|
||||
contractor_name = db.Column(db.String(255), nullable=False, index=True)
|
||||
contractor_nip = db.Column(db.String(32), index=True)
|
||||
contractor_regon = db.Column(db.String(32), index=True)
|
||||
contractor_address = db.Column(db.String(512))
|
||||
|
||||
issue_date = db.Column(db.Date, nullable=False, index=True)
|
||||
received_date = db.Column(db.Date, index=True)
|
||||
fetched_at = db.Column(db.DateTime, index=True)
|
||||
|
||||
net_amount = db.Column(db.Numeric(12, 2), nullable=False, default=0)
|
||||
vat_amount = db.Column(db.Numeric(12, 2), nullable=False, default=0)
|
||||
gross_amount = db.Column(db.Numeric(12, 2), nullable=False, default=0)
|
||||
split_payment = db.Column(db.Boolean, default=False, nullable=False)
|
||||
currency = db.Column(db.String(8), default='PLN')
|
||||
seller_bank_account = db.Column(db.String(64), default='')
|
||||
|
||||
invoice_type = db.Column(db.Enum(InvoiceType), nullable=False, default=InvoiceType.PURCHASE)
|
||||
status = db.Column(db.Enum(InvoiceStatus), nullable=False, default=InvoiceStatus.NEW)
|
||||
|
||||
xml_path = db.Column(db.String(512))
|
||||
pdf_path = db.Column(db.String(512))
|
||||
html_preview = db.Column(db.Text)
|
||||
internal_note = db.Column(db.Text)
|
||||
source_hash = db.Column(db.String(128))
|
||||
read_at = db.Column(db.DateTime)
|
||||
last_synced_at = db.Column(db.DateTime)
|
||||
|
||||
external_metadata = db.Column(db.JSON, default=dict)
|
||||
|
||||
is_unread = db.Column(db.Boolean, default=True, nullable=False)
|
||||
pinned = db.Column(db.Boolean, default=False, nullable=False)
|
||||
queue_accounting = db.Column(db.Boolean, default=False, nullable=False)
|
||||
source = db.Column(db.String(32), default='ksef', nullable=False)
|
||||
|
||||
customer_id = db.Column(db.Integer, db.ForeignKey('customer.id'), index=True)
|
||||
|
||||
issued_to_ksef_at = db.Column(db.DateTime)
|
||||
issued_status = db.Column(db.String(32), default='received', nullable=False)
|
||||
|
||||
tags = db.relationship(
|
||||
'Tag',
|
||||
secondary=invoice_tags,
|
||||
lazy='joined',
|
||||
backref=db.backref('invoices', lazy='dynamic'),
|
||||
)
|
||||
sync_events = db.relationship(
|
||||
'SyncEvent',
|
||||
backref='invoice',
|
||||
lazy='dynamic',
|
||||
cascade='all, delete-orphan',
|
||||
)
|
||||
mail_deliveries = db.relationship(
|
||||
'MailDelivery',
|
||||
backref='invoice',
|
||||
lazy='dynamic',
|
||||
cascade='all, delete-orphan',
|
||||
)
|
||||
notifications = db.relationship(
|
||||
'NotificationLog',
|
||||
backref='invoice',
|
||||
lazy='dynamic',
|
||||
cascade='all, delete-orphan',
|
||||
)
|
||||
company = db.relationship('Company', backref=db.backref('invoices', lazy='dynamic'))
|
||||
customer = db.relationship('Customer', backref=db.backref('invoices', lazy='dynamic'))
|
||||
|
||||
__table_args__ = (
|
||||
db.UniqueConstraint('company_id', 'ksef_number', name='uq_invoice_company_ksef'),
|
||||
)
|
||||
|
||||
@property
|
||||
def month_key(self):
|
||||
return f'{self.issue_date.year}-{self.issue_date.month:02d}'
|
||||
|
||||
@property
|
||||
def invoice_type_label(self):
|
||||
return INVOICE_TYPE_LABELS.get(self.invoice_type, getattr(self.invoice_type, 'value', self.invoice_type))
|
||||
|
||||
@property
|
||||
def status_label(self):
|
||||
return INVOICE_STATUS_LABELS.get(self.status, getattr(self.status, 'value', self.status))
|
||||
|
||||
@property
|
||||
def issued_status_label(self):
|
||||
return ISSUED_STATUS_LABELS.get(self.issued_status, self.issued_status)
|
||||
|
||||
|
||||
class Tag(TimestampMixin, db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(64), unique=True, nullable=False)
|
||||
color = db.Column(db.String(32), default='secondary')
|
||||
|
||||
|
||||
class SyncEvent(TimestampMixin, db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
invoice_id = db.Column(db.Integer, db.ForeignKey('invoice.id'), nullable=False)
|
||||
status = db.Column(db.String(32), nullable=False)
|
||||
message = db.Column(db.Text)
|
||||
source = db.Column(db.String(32), default='ksef')
|
||||
|
||||
|
||||
class MailDelivery(TimestampMixin, db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
invoice_id = db.Column(db.Integer, db.ForeignKey('invoice.id'), nullable=False)
|
||||
recipient = db.Column(db.String(255), nullable=False)
|
||||
status = db.Column(db.String(32), default='queued')
|
||||
subject = db.Column(db.String(255))
|
||||
error_message = db.Column(db.Text)
|
||||
sent_at = db.Column(db.DateTime)
|
||||
|
||||
|
||||
class NotificationLog(TimestampMixin, db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
invoice_id = db.Column(db.Integer, db.ForeignKey('invoice.id'))
|
||||
channel = db.Column(db.String(32), nullable=False)
|
||||
status = db.Column(db.String(32), default='queued')
|
||||
message = db.Column(db.Text)
|
||||
sent_at = db.Column(db.DateTime)
|
||||
Reference in New Issue
Block a user