/akvo/rsr/models/organisation_budget.py

https://github.com/akvo/akvo-rsr · Python · 288 lines · 226 code · 57 blank · 5 comment · 9 complexity · 74125ea02de088df17a7e4551f289819 MD5 · raw file

  1. # -*- coding: utf-8 -*-
  2. # Akvo RSR is covered by the GNU Affero General Public License.
  3. # See more details in the license.txt file located at the root folder of the Akvo RSR module.
  4. # For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.
  5. from akvo.codelists.models import BudgetStatus, Country, Currency, Region, RegionVocabulary
  6. from akvo.codelists.store.default_codelists import (BUDGET_STATUS, COUNTRY, CURRENCY, REGION,
  7. REGION_VOCABULARY)
  8. from akvo.rsr.fields import ValidXMLCharField
  9. from akvo.utils import codelist_choices, codelist_value
  10. from django.core.exceptions import ValidationError
  11. from django.db import models
  12. from django.utils.translation import ugettext_lazy as _
  13. class OrganisationFinanceBasic(models.Model):
  14. currency = ValidXMLCharField(
  15. _('currency'), max_length=3, blank=True, choices=codelist_choices(CURRENCY)
  16. )
  17. value = models.DecimalField(
  18. _('value'), max_digits=20, decimal_places=2, null=True, blank=True,
  19. help_text=_('Enter the amount of budget that is set aside for this specific budget. '
  20. 'Use a period to denote decimals.')
  21. )
  22. value_date = models.DateField(
  23. _('value date'), null=True, blank=True,
  24. help_text=_('Enter the date (DD/MM/YYYY) to be used for determining the exchange rate for '
  25. 'currency conversions.')
  26. )
  27. period_start = models.DateField(
  28. _('period start'), null=True, blank=True,
  29. help_text=_('Enter the start date (DD/MM/YYYY) for the budget period.')
  30. )
  31. period_end = models.DateField(
  32. _('period end'), null=True, blank=True,
  33. help_text=_('Enter the end date (DD/MM/YYYY) for the budget period.')
  34. )
  35. class Meta:
  36. app_label = 'rsr'
  37. abstract = True
  38. def __str__(self):
  39. if self.value and self.currency:
  40. return '%s %s' % (self.currency, '{:,}'.format(int(self.value)))
  41. else:
  42. return '%s' % _('No currency or value specified')
  43. def clean(self):
  44. # Don't allow a start date before an end date
  45. if self.period_start and self.period_end and (self.period_start > self.period_end):
  46. raise ValidationError(
  47. {
  48. 'period_start': '%s' % _('Period start cannot be at a later time than period '
  49. 'end.'),
  50. 'period_end': '%s' % _('Period start cannot be at a later time than period '
  51. 'end.')
  52. }
  53. )
  54. def iati_currency(self):
  55. return codelist_value(Currency, self, 'currency')
  56. def iati_currency_unicode(self):
  57. return str(self.iati_currency())
  58. class OrganisationBudget(OrganisationFinanceBasic):
  59. status = ValidXMLCharField(
  60. _('status'), max_length=1, blank=True, choices=codelist_choices(BUDGET_STATUS),
  61. help_text=_('The status explains whether the budget being reported is indicative or has '
  62. 'been formally committed.')
  63. )
  64. class Meta:
  65. app_label = 'rsr'
  66. abstract = True
  67. def iati_status(self):
  68. return codelist_value(BudgetStatus, self, 'status')
  69. def iati_status_unicode(self):
  70. return str(self.iati_status())
  71. class OrganisationTotalBudget(OrganisationBudget):
  72. organisation = models.ForeignKey(
  73. 'Organisation', on_delete=models.CASCADE, verbose_name=_('organisation'), related_name='total_budgets'
  74. )
  75. class Meta:
  76. app_label = 'rsr'
  77. verbose_name = _('organisation total budget')
  78. verbose_name_plural = _('organisation total budgets')
  79. class OrganisationRecipientOrgBudget(OrganisationBudget):
  80. organisation = models.ForeignKey(
  81. 'Organisation', on_delete=models.CASCADE, verbose_name=_('organisation'), related_name='recipient_org_budgets'
  82. )
  83. recipient_organisation = models.ForeignKey(
  84. 'Organisation', on_delete=models.CASCADE, verbose_name=_('recipient organisation'),
  85. related_name='receiver_org_budgets'
  86. )
  87. class Meta:
  88. app_label = 'rsr'
  89. verbose_name = _('recipient organisation budget')
  90. verbose_name_plural = _('recipient organisation budgets')
  91. class OrganisationRegionBudget(OrganisationBudget):
  92. organisation = models.ForeignKey(
  93. 'Organisation', on_delete=models.CASCADE, verbose_name=_('organisation'), related_name='recipient_region_budgets'
  94. )
  95. region = ValidXMLCharField(
  96. _('recipient region'), blank=True, max_length=25, choices=codelist_choices(REGION),
  97. help_text=_('This identifies the region which concerns the organisation budget.')
  98. )
  99. region_vocabulary = ValidXMLCharField(
  100. _('vocabulary'), blank=True, max_length=2,
  101. choices=codelist_choices(REGION_VOCABULARY),
  102. help_text=_('The vocabulary from which the region code is drawn. If it is not present 1 – '
  103. '\'OECD DAC\' is assumed.')
  104. )
  105. region_vocabulary_uri = ValidXMLCharField(
  106. _('vocabulary URI'), blank=True, max_length=1000,
  107. help_text=_('If the vocabulary is 99 (reporting organisation), the URI where this '
  108. 'internal vocabulary is defined.')
  109. )
  110. text = ValidXMLCharField(
  111. _('description'), blank=True, max_length=100,
  112. help_text=_('Optionally enter a short description.')
  113. )
  114. class Meta:
  115. app_label = 'rsr'
  116. verbose_name = _('organisation recipient region budget')
  117. verbose_name_plural = _('organisation recipient region budgets')
  118. def iati_region(self):
  119. return codelist_value(Region, self, 'region')
  120. def iati_region_unicode(self):
  121. return str(self.iati_region())
  122. def iati_region_vocabulary(self):
  123. return codelist_value(RegionVocabulary, self, 'region_vocabulary')
  124. def iati_region_vocabulary_unicode(self):
  125. return str(self.iati_region_vocabulary())
  126. class OrganisationCountryBudget(OrganisationBudget):
  127. organisation = models.ForeignKey(
  128. 'Organisation', on_delete=models.CASCADE, verbose_name=_('organisation'), related_name='recipient_country_budgets'
  129. )
  130. country = ValidXMLCharField(
  131. _('recipient country'), blank=True, max_length=2, choices=codelist_choices(COUNTRY, show_code=False),
  132. help_text=_('This identifies the country which concerns the organisation budget.')
  133. )
  134. text = ValidXMLCharField(
  135. _('description'), blank=True, max_length=100,
  136. help_text=_('Optionally enter a short description.')
  137. )
  138. class Meta:
  139. app_label = 'rsr'
  140. verbose_name = _('organisation recipient country budget')
  141. verbose_name_plural = _('organisation recipient country budgets')
  142. def iati_country(self):
  143. return codelist_value(Country, self, 'country')
  144. def iati_country_unicode(self):
  145. return str(self.iati_country())
  146. class OrganisationTotalExpenditure(OrganisationFinanceBasic):
  147. organisation = models.ForeignKey(
  148. 'Organisation', on_delete=models.CASCADE, verbose_name=_('organisation'), related_name='total_expenditures'
  149. )
  150. class Meta:
  151. app_label = 'rsr'
  152. verbose_name = _('organisation total expenditure')
  153. verbose_name_plural = _('organisation total expenditures')
  154. class LineBasic(models.Model):
  155. currency = ValidXMLCharField(
  156. _('currency'), max_length=3, blank=True, choices=codelist_choices(CURRENCY)
  157. )
  158. value = models.DecimalField(
  159. _('value'), max_digits=20, decimal_places=2, null=True, blank=True,
  160. help_text=_('Enter the amount of this specific line. Use a period to denote decimals.')
  161. )
  162. value_date = models.DateField(
  163. _('value date'), null=True, blank=True,
  164. help_text=_('Enter the date (DD/MM/YYYY) to be used for determining the exchange rate for '
  165. 'currency conversions.')
  166. )
  167. reference = ValidXMLCharField(
  168. _('reference'), blank=True, max_length=50,
  169. help_text=_('An internal reference that describes the line in the reporting '
  170. 'organisation\'s own system')
  171. )
  172. text = ValidXMLCharField(
  173. _('description'), blank=True, max_length=1000,
  174. help_text=_('The description for this line.')
  175. )
  176. class Meta:
  177. app_label = 'rsr'
  178. abstract = True
  179. def __str__(self):
  180. if self.value and self.currency:
  181. return '%s %s' % (self.currency, '{:,}'.format(int(self.value)))
  182. else:
  183. return '%s' % _('No currency or value specified')
  184. def iati_currency(self):
  185. return codelist_value(Currency, self, 'currency')
  186. def iati_currency_unicode(self):
  187. return str(self.iati_currency())
  188. class OrganisationTotalBudgetLine(LineBasic):
  189. budget = models.ForeignKey(
  190. OrganisationTotalBudget, on_delete=models.CASCADE, verbose_name=_('organisation budget'), related_name='budget_lines'
  191. )
  192. class Meta:
  193. app_label = 'rsr'
  194. verbose_name = _('total budget line')
  195. verbose_name_plural = _('total budget lines')
  196. class OrganisationRecipientOrgBudgetLine(LineBasic):
  197. budget = models.ForeignKey(
  198. OrganisationRecipientOrgBudget, on_delete=models.CASCADE, verbose_name=_('organisation budget'),
  199. related_name='budget_lines'
  200. )
  201. class Meta:
  202. app_label = 'rsr'
  203. verbose_name = _('recipient organisation budget line')
  204. verbose_name_plural = _('recipient organisation budget lines')
  205. class OrganisationRegionBudgetLine(LineBasic):
  206. budget = models.ForeignKey(
  207. OrganisationRegionBudget, on_delete=models.CASCADE, verbose_name=_('organisation budget'),
  208. related_name='budget_lines'
  209. )
  210. class Meta:
  211. app_label = 'rsr'
  212. verbose_name = _('region budget line')
  213. verbose_name_plural = _('region budget lines')
  214. class OrganisationCountryBudgetLine(LineBasic):
  215. budget = models.ForeignKey(
  216. OrganisationCountryBudget, on_delete=models.CASCADE, verbose_name=_('organisation budget'),
  217. related_name='budget_lines'
  218. )
  219. class Meta:
  220. app_label = 'rsr'
  221. verbose_name = _('country budget line')
  222. verbose_name_plural = _('country budget lines')
  223. class OrganisationExpenseLine(LineBasic):
  224. expenditure = models.ForeignKey(
  225. OrganisationTotalExpenditure, on_delete=models.CASCADE, verbose_name=_('organisation expenditure'),
  226. related_name='expense_lines'
  227. )
  228. class Meta:
  229. app_label = 'rsr'
  230. verbose_name = _('expense line')
  231. verbose_name_plural = _('expense lines')