您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

8 年前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  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. Translate -- whether to use english translations for various text blocks
  17. """
  18. data_template = """\
  19. ItemHeading: Faktúrujeme Vám za služby podľa Rámcovej zmluvy
  20. Item: Description|Amount|Unit|Rate
  21. Issued: {0}
  22. Delivered: {0}
  23. Due:
  24. Advance: 0
  25. Translate: 0
  26. """.format(datetime.datetime.today().strftime("%Y-%m-%d"))
  27. _directory = "income"
  28. _regex = re.compile("^(?P<date>[0-9]{8})-(?P<number>[0-9]{3})-(?P<company_name>[a-z0-9-]+)$")
  29. _template = "{date}-{number:03}-{company_name}"
  30. def _item_class(self):
  31. return Invoice
  32. def _select(self, selector):
  33. if isinstance(selector, str):
  34. match = self._regex.match(selector)
  35. if match:
  36. selector = match.groupdict()
  37. selector["number"] = int(selector["number"])
  38. else:
  39. try:
  40. selector = int(selector)
  41. except TypeError:
  42. raise ItemNotFoundError("Item not found: {0}".format(selector))
  43. return super(Invoices, self)._select(selector)
  44. def new(self, company_name):
  45. if company_name not in self._db.companies:
  46. raise ItemNotFoundError("Company '{0}' not found.".format(company_name))
  47. try:
  48. number = max(item.number for item in self) + 1
  49. except ValueError:
  50. number = 1
  51. date = time.strftime("%Y%m%d")
  52. name = self._template.format(**vars())
  53. return super(Invoices, self).new(name)
  54. class Invoice(Item):
  55. def _data_class(self):
  56. return InvoiceData
  57. def _postprocess(self):
  58. self._selector["number"] = int(self.number)
  59. class InvoiceData(Data):
  60. _fields = ["issued", "due", "delivered", "paid", "payment", "advance", "translate"]
  61. _multivalue_fields = ["itemheading", "item", "address", "note"]
  62. _date_regex = re.compile(r"^(\d{4})-?(\d{2})-?(\d{2})$")
  63. _item_regex = re.compile(r"^([^|]*)\|(-?\d+)\|([^|]*)\|(-?\d+)$")
  64. _number_template = "{year}{number:03}"
  65. def _parse_date(self, date):
  66. match = self._date_regex.match(date)
  67. if not match:
  68. raise ValueError("Bad date format: {0}".format(date))
  69. log.debug("Date match: {0}".format(match.groups()))
  70. return datetime.date(*(int(f) for f in match.groups()))
  71. def _postprocess(self):
  72. log.debug(self._data)
  73. self._postprocess_number()
  74. self._postprocess_items()
  75. self._postprocess_dates()
  76. self.rename_key("note", "notes")
  77. def _postprocess_number(self):
  78. self._data["number"] = self._number_template.format(
  79. year = self.year,
  80. number = self.number)
  81. def _postprocess_items(self):
  82. items = []
  83. for item in self.item:
  84. match = self._item_regex.match(item)
  85. if not match:
  86. raise ValueError("Bad item format: {0}".format(item))
  87. description, amount, unit, rate = match.groups()
  88. items.append((description, float(amount), unit, float(rate)))
  89. del self._data["item"]
  90. m_advance = float(self.advance or 0)
  91. m_sum = sum(item[1] * item[3] for item in items)
  92. self._data["items"] = items
  93. self._data["sum"] = m_sum
  94. self._data["advance"] = m_advance
  95. self._data["topay"] = m_sum - m_advance
  96. self._data["translate"] = bool(int(self.translate))
  97. def _postprocess_dates(self):
  98. if self.issued:
  99. try:
  100. issued = self._parse_date(self.issued)
  101. except ValueError:
  102. raise ValueError("Bad issued format: {0}".format(self.issued))
  103. else:
  104. issued = datetime.datetime.now()
  105. if self.delivered:
  106. try:
  107. delivered = self._parse_date(self.delivered)
  108. except ValueError:
  109. raise ValueError("Bad delivered format: {0}".format(self.delivered))
  110. else:
  111. delivered = datetime.datetime.now()
  112. if self.due:
  113. try:
  114. due = self._parse_date(self.due)
  115. except ValueError:
  116. try:
  117. due = datetime.timedelta(int(re.sub("^\+", "", self.due)))
  118. except ValueError:
  119. raise ValueError("Bad due format: {0}".format(self.due))
  120. else:
  121. due = datetime.timedelta(14)
  122. log.debug("Issued : {0}".format(issued))
  123. log.debug("Delivered: {0}".format(delivered))
  124. log.debug("Due : {0}".format(due))
  125. if isinstance(due, datetime.timedelta):
  126. due += issued
  127. self._data["issued"] = issued
  128. self._data["delivered"] = delivered
  129. self._data["due"] = due