1
1
"""Object model."""
2
2
3
- import email
4
3
from typing import Optional , List , Dict , Type
5
4
from pydantic import constr , BaseModel , condecimal
6
5
from enum import Enum
7
6
import datetime
8
- import hashlib
9
- import uuid
10
7
import textwrap
11
8
12
9
import sqlalchemy
@@ -50,9 +47,10 @@ def to_dataframe(items: List[Type[BaseModel]]) -> pandas.DataFrame:
50
47
51
48
52
49
def OneToOneRelationship (back_populates ):
50
+ """Define a relationship as one-to-one."""
53
51
return Relationship (
54
52
back_populates = back_populates ,
55
- sa_relationship_kwargs = {"uselist" : False },
53
+ sa_relationship_kwargs = {"uselist" : False , "lazy" : "subquery" },
56
54
)
57
55
58
56
@@ -150,6 +148,11 @@ class User(SQLModel, table=True):
150
148
)
151
149
# TODO: path to logo image
152
150
logo : Optional [str ]
151
+ # User 1:n Invoices
152
+ # invoices: List["Invoice"] = Relationship(
153
+ # back_populates="user",
154
+ # sa_relationship_kwargs={"lazy": "subquery"},
155
+ # )
153
156
154
157
@property
155
158
def bank_account_not_set (self ) -> bool :
@@ -454,10 +457,7 @@ class Timesheet(SQLModel, table=True):
454
457
id : Optional [int ] = Field (default = None , primary_key = True )
455
458
title : str
456
459
date : datetime .date = Field (description = "The date of creation of the timesheet" )
457
- # period: str
458
- # table: pandas.DataFrame
459
- # TODO: store dataframe as dict
460
- # table: Dict = Field(default={}, sa_column=sqlalchemy.Column(sqlalchemy.JSON))
460
+
461
461
# Timesheet n:1 Project
462
462
project_id : Optional [int ] = Field (default = None , foreign_key = "project.id" )
463
463
project : Project = Relationship (
@@ -469,6 +469,18 @@ class Timesheet(SQLModel, table=True):
469
469
comment : Optional [str ] = Field (description = "A comment on the timesheet." )
470
470
items : List [TimeTrackingItem ] = Relationship (back_populates = "timesheet" )
471
471
472
+ rendered : bool = Field (
473
+ default = False ,
474
+ description = "Whether the Timesheet has been rendered as a PDF." ,
475
+ )
476
+
477
+ # Timesheet 1:1 Invoice
478
+ # FIXME: Could not determine join condition between parent/child tables
479
+ # invoice_id: Optional[int] = Field(default=None, foreign_key="invoice.id")
480
+ # invoice: Optional["Invoice"] = OneToOneRelationship(
481
+ # back_populates="timesheet",
482
+ # )
483
+
472
484
# class Config:
473
485
# arbitrary_types_allowed = True
474
486
@@ -492,16 +504,18 @@ class Invoice(SQLModel, table=True):
492
504
"""An invoice is a bill for a client."""
493
505
494
506
id : Optional [int ] = Field (default = None , primary_key = True )
495
- number : str
507
+ number : Optional [ str ] = Field ( description = "The invoice number. Auto-generated." )
496
508
# date and time
497
509
date : datetime .date = Field (
498
510
description = "The date of the invoice" ,
499
511
)
500
- # due_date: datetime.date
501
- # sent_date: datetime.date
502
- # Invoice 1:n Timesheet ?
512
+
513
+ # TODO: sent_date: datetime.datetime = Field(description="The date the invoice was sent.")
514
+ # Invoice 1:1 Timesheet
503
515
# timesheet_id: Optional[int] = Field(default=None, foreign_key="timesheet.id")
504
- # timesheet: Timesheet = Relationship(back_populates="invoice")
516
+ # timesheet: Timesheet = OneToOneRelationship(
517
+ # back_populates="invoice",
518
+ # )
505
519
# Invoice n:1 Contract ?
506
520
contract_id : Optional [int ] = Field (default = None , foreign_key = "contract.id" )
507
521
contract : Contract = Relationship (
@@ -529,7 +543,7 @@ class Invoice(SQLModel, table=True):
529
543
)
530
544
rendered : bool = Field (
531
545
default = False ,
532
- description = "If the invoice has been rendered as a PDF." ,
546
+ description = "Whether the invoice has been rendered as a PDF." ,
533
547
)
534
548
535
549
#
@@ -548,7 +562,7 @@ def total(self) -> Decimal:
548
562
"""Total invoiced amount."""
549
563
return self .sum + self .VAT_total
550
564
551
- def generate_number (self , pattern = None , counter = None ):
565
+ def generate_number (self , pattern = None , counter = None ) -> str :
552
566
"""Generate an invoice number"""
553
567
date_prefix = self .date .strftime ("%Y-%m-%d" )
554
568
# suffix = hashlib.shake_256(str(uuid.uuid4()).encode("utf-8")).hexdigest(2)
@@ -559,9 +573,12 @@ def generate_number(self, pattern=None, counter=None):
559
573
self .number = f"{ date_prefix } -{ suffix } "
560
574
561
575
@property
562
- def due_date (self ):
576
+ def due_date (self ) -> Optional [ datetime . date ] :
563
577
"""Date until which payment is due."""
564
- return self .date + datetime .timedelta (days = self .contract .term_of_payment )
578
+ if self .contract .term_of_payment :
579
+ return self .date + datetime .timedelta (days = self .contract .term_of_payment )
580
+ else :
581
+ return None
565
582
566
583
@property
567
584
def client (self ):
0 commit comments