25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

invoices.py 4.8KB

8 yıl önce
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. #!/usr/bin/python3
  2. import os, sys, re, time, datetime
  3. import logging
  4. log = logging.getLogger()
  5. from invoice.db.base import *
  6. class Invoices(List):
  7. """Company invoice.
  8. When editing data files, you can use the following
  9. directives:
  10. Item -- Description|Amount|Unit|Rate
  11. Issued -- Date when the invoice was issued in YYYY-MM-DD format
  12. Due -- Due date YYYY-MM-DD
  13. Delivered -- Date when the services were delivered in YYYY-MM-DD format
  14. Note -- a note at the and of the invoice
  15. Advance -- amount of money already paid
  16. """
  17. data_template = """\
  18. Item:Description|Amount|Unit|Rate
  19. Issued:
  20. Delivered:
  21. Due:
  22. Advance: 0
  23. """
  24. _directory = "income"
  25. _regex = re.compile("^(?P<date>[0-9]{8})-(?P<number>[0-9]{3})-(?P<company_name>[a-z0-9-]+)$")
  26. _template = "{date}-{number:03}-{company_name}"
  27. def _item_class(self):
  28. return Invoice
  29. def _select(self, selector):
  30. if isinstance(selector, str):
  31. match = self._regex.match(selector)
  32. if match:
  33. selector = match.groupdict()
  34. selector["number"] = int(selector["number"])
  35. else:
  36. try:
  37. selector = int(selector)
  38. except TypeError:
  39. raise ItemNotFoundError("Item not found: {0}".format(selector))
  40. return super(Invoices, self)._select(selector)
  41. def new(self, company_name):
  42. if company_name not in self._db.companies:
  43. raise ItemNotFoundError("Company '{0}' not found.".format(company_name))
  44. try:
  45. number = max(item.number for item in self) + 1
  46. except ValueError:
  47. number = 1
  48. date = time.strftime("%Y%m%d")
  49. name = self._template.format(**vars())
  50. return super(Invoices, self).new(name)
  51. class Invoice(Item):
  52. def _data_class(self):
  53. return InvoiceData
  54. def _postprocess(self):
  55. self._selector["number"] = int(self.number)
  56. class InvoiceData(Data):
  57. _fields = ["issued", "due", "delivered", "paid", "payment", "advance"]
  58. _multivalue_fields = ["item", "address", "note"]
  59. _date_regex = re.compile(r"^(\d{4})-?(\d{2})-?(\d{2})$")
  60. _item_regex = re.compile(r"^([^|]*)\|(-?\d+)\|([^|]*)\|(-?\d+)$")
  61. _number_template = "{year}{number:03}"
  62. def _parse_date(self, date):
  63. match = self._date_regex.match(date)
  64. if not match:
  65. raise ValueError("Bad date format: {0}".format(date))
  66. log.debug("Date match: {0}".format(match.groups()))
  67. return datetime.date(*(int(f) for f in match.groups()))
  68. def _postprocess(self):
  69. log.debug(self._data)
  70. self._postprocess_number()
  71. self._postprocess_items()
  72. self._postprocess_dates()
  73. self.rename_key("note", "notes")
  74. def _postprocess_number(self):
  75. self._data["number"] = self._number_template.format(
  76. year = self.year,
  77. number = self.number)
  78. def _postprocess_items(self):
  79. items = []
  80. for item in self.item:
  81. match = self._item_regex.match(item)
  82. if not match:
  83. raise ValueError("Bad item format: {0}".format(item))
  84. description, amount, unit, rate = match.groups()
  85. items.append((description, float(amount), unit, float(rate)))
  86. del self._data["item"]
  87. m_advance = float(self.advance or 0)
  88. m_sum = sum(item[1] * item[3] for item in items)
  89. self._data["items"] = items
  90. self._data["sum"] = m_sum
  91. self._data["advance"] = m_advance
  92. self._data["topay"] = m_sum - m_advance
  93. def _postprocess_dates(self):
  94. if self.issued:
  95. try:
  96. issued = self._parse_date(self.issued)
  97. except ValueError:
  98. raise ValueError("Bad issued format: {0}".format(self.issued))
  99. else:
  100. issued = datetime.datetime.now()
  101. if self.delivered:
  102. try:
  103. delivered = self._parse_date(self.delivered)
  104. except ValueError:
  105. raise ValueError("Bad delivered format: {0}".format(self.delivered))
  106. else:
  107. delivered = datetime.datetime.now()
  108. if self.due:
  109. try:
  110. due = self._parse_date(self.due)
  111. except ValueError:
  112. try:
  113. due = datetime.timedelta(int(re.sub("^\+", "", self.due)))
  114. except ValueError:
  115. raise ValueError("Bad due format: {0}".format(self.due))
  116. else:
  117. due = datetime.timedelta(14)
  118. log.debug("Issued : {0}".format(issued))
  119. log.debug("Delivered: {0}".format(delivered))
  120. log.debug("Due : {0}".format(due))
  121. if isinstance(due, datetime.timedelta):
  122. due += issued
  123. self._data["issued"] = issued
  124. self._data["delivered"] = delivered
  125. self._data["due"] = due