|
19 | 19 | >>> bill_model.save() |
20 | 20 | """ |
21 | 21 |
|
22 | | -from datetime import date |
| 22 | +from datetime import date, datetime |
23 | 23 | from decimal import Decimal |
24 | 24 | from typing import Union, Optional, Tuple, Dict, List |
25 | 25 | from uuid import uuid4 |
|
31 | 31 | from django.db.models.signals import post_delete, pre_save |
32 | 32 | from django.shortcuts import get_object_or_404 |
33 | 33 | from django.urls import reverse |
34 | | -from django.utils.timezone import localdate |
| 34 | +from django.utils.timezone import localdate, localtime |
35 | 35 | from django.utils.translation import gettext_lazy as _ |
36 | 36 |
|
37 | 37 | from django_ledger.models.entity import EntityModel |
@@ -906,16 +906,88 @@ def can_generate_bill_number(self) -> bool: |
906 | 906 | self.is_configured() |
907 | 907 | ]) |
908 | 908 |
|
909 | | - # --> URLs <--- |
910 | | - def get_absolute_url(self): |
911 | | - return reverse('django_ledger:bill-detail', |
912 | | - kwargs={ |
913 | | - 'entity_slug': self.ledger.entity.slug, |
914 | | - 'bill_pk': self.uuid |
915 | | - }) |
| 909 | + # ACTIONS --- |
| 910 | + |
| 911 | + def can_make_payment(self) -> bool: |
| 912 | + """ |
| 913 | + Checks if the BillModel can accept a payment. |
| 914 | +
|
| 915 | +
|
| 916 | + Returns |
| 917 | + _______ |
| 918 | +
|
| 919 | + bool |
| 920 | + True if can bind provided PurchaseOderModel, else False. |
| 921 | + """ |
| 922 | + return self.is_approved() |
916 | 923 |
|
917 | | - # --> ACTIONS <--- |
918 | | - def action_bind_estimate(self, estimate_model, commit: bool = False, raise_exception: bool = True): |
| 924 | + def make_payment(self, |
| 925 | + payment_amount: Union[Decimal, float, int], |
| 926 | + payment_date: Optional[Union[datetime, date]] = None, |
| 927 | + commit: bool = False, |
| 928 | + raise_exception: bool = True): |
| 929 | + """ |
| 930 | + Makes a payment to the BillModel. |
| 931 | +
|
| 932 | +
|
| 933 | + Parameters |
| 934 | + __________ |
| 935 | +
|
| 936 | + payment_amount: Decimal ot float |
| 937 | + The payment amount to process. |
| 938 | +
|
| 939 | + payment_date: datetime or date. |
| 940 | + Date or timestamp of the payment being applied. |
| 941 | +
|
| 942 | + commit: bool |
| 943 | + If True, commits the transaction into the DB. Defaults to False. |
| 944 | +
|
| 945 | + raise_exception: bool |
| 946 | + If True, raises BillModelValidationError if payment exceeds amount due, else False. |
| 947 | +
|
| 948 | + Returns |
| 949 | + _______ |
| 950 | +
|
| 951 | + bool |
| 952 | + True if can make payment, else False. |
| 953 | + """ |
| 954 | + |
| 955 | + if isinstance(payment_amount, float): |
| 956 | + payment_amount = Decimal.from_float(payment_amount) |
| 957 | + elif isinstance(payment_amount, int): |
| 958 | + payment_amount = Decimal.from_float(float(payment_amount)) |
| 959 | + self.amount_paid += payment_amount |
| 960 | + |
| 961 | + if self.amount_paid > self.amount_due: |
| 962 | + if raise_exception: |
| 963 | + raise BillModelValidationError( |
| 964 | + f'Amount paid: {self.amount_paid} exceed amount due: {self.amount_due}.' |
| 965 | + ) |
| 966 | + return |
| 967 | + |
| 968 | + self.get_state(commit=True) |
| 969 | + self.clean() |
| 970 | + |
| 971 | + if not payment_date: |
| 972 | + payment_date = localtime() |
| 973 | + |
| 974 | + if commit: |
| 975 | + self.migrate_state( |
| 976 | + user_model=None, |
| 977 | + entity_slug=self.ledger.entity.slug, |
| 978 | + je_timestamp=payment_date, |
| 979 | + raise_exception=True |
| 980 | + ) |
| 981 | + self.save( |
| 982 | + update_fields=[ |
| 983 | + 'amount_paid', |
| 984 | + 'amount_earned', |
| 985 | + 'amount_unearned', |
| 986 | + 'amount_receivable', |
| 987 | + 'updated' |
| 988 | + ]) |
| 989 | + |
| 990 | + def bind_estimate(self, estimate_model, commit: bool = False, raise_exception: bool = True): |
919 | 991 | """ |
920 | 992 | Binds BillModel to a given EstimateModel. Raises ValueError if EstimateModel cannot be bound. |
921 | 993 |
|
@@ -1168,7 +1240,7 @@ def mark_as_approved(self, |
1168 | 1240 | self.migrate_state( |
1169 | 1241 | entity_slug=entity_slug, |
1170 | 1242 | user_model=user_model, |
1171 | | - je_date=date_approved, |
| 1243 | + je_timestamp=date_approved, |
1172 | 1244 | force_migrate=self.accrue |
1173 | 1245 | ) |
1174 | 1246 | self.ledger.post(commit=commit) |
@@ -1226,6 +1298,7 @@ def mark_as_paid(self, |
1226 | 1298 | itemtxs_qs: Optional[ItemTransactionModelQuerySet] = None, |
1227 | 1299 | commit: bool = False, |
1228 | 1300 | **kwargs): |
| 1301 | + |
1229 | 1302 | """ |
1230 | 1303 | Marks BillModel as Paid. |
1231 | 1304 |
|
@@ -1290,7 +1363,7 @@ def mark_as_paid(self, |
1290 | 1363 | user_model=user_model, |
1291 | 1364 | entity_slug=entity_slug, |
1292 | 1365 | itemtxs_qs=itemtxs_qs, |
1293 | | - je_date=date_paid, |
| 1366 | + je_timestamp=date_paid, |
1294 | 1367 | force_migrate=True |
1295 | 1368 | ) |
1296 | 1369 | self.lock_ledger(commit=True) |
@@ -1725,6 +1798,14 @@ def generate_bill_number(self, commit: bool = False) -> str: |
1725 | 1798 | def generate_descriptive_title(self) -> str: |
1726 | 1799 | return f'Bill {self.bill_number} | {self.get_bill_status_display()} {self.get_status_action_date()} | {self.vendor.vendor_name}' |
1727 | 1800 |
|
| 1801 | + # --> URLs <--- |
| 1802 | + def get_absolute_url(self): |
| 1803 | + return reverse('django_ledger:bill-detail', |
| 1804 | + kwargs={ |
| 1805 | + 'entity_slug': self.ledger.entity.slug, |
| 1806 | + 'bill_pk': self.uuid |
| 1807 | + }) |
| 1808 | + |
1728 | 1809 | def clean(self, commit: bool = True): |
1729 | 1810 | """ |
1730 | 1811 | Clean method for BillModel. Results in a DB query if bill number has not been generated and the BillModel is |
|
0 commit comments