Source code for akvo.rsr.models.budget_item

# -*- coding: utf-8 -*-

# Akvo RSR is covered by the GNU Affero General Public License.
# See more details in the license.txt file located at the root folder of the Akvo RSR module.
# For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.


from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.utils.translation import gettext_lazy as _

from ..fields import ValidXMLCharField

from akvo.codelists.models import BudgetIdentifier, BudgetStatus, BudgetType, Currency
from akvo.codelists.store.default_codelists import (BUDGET_IDENTIFIER, BUDGET_TYPE, BUDGET_STATUS,
                                                    CURRENCY)
from akvo.utils import codelist_choices, codelist_value


[docs]class BudgetItemLabel(models.Model): TOTAL_BUDGET_LABEL_ID = 14 label = ValidXMLCharField(_('label'), max_length=30, unique=True, db_index=True) def __str__(self): return self.label class Meta: app_label = 'rsr' ordering = ('label',) verbose_name = _('budget item label') verbose_name_plural = _('budget item labels')
[docs]class BudgetItem(models.Model): # DON'T translate. Need model translations for this to work OTHER_LABELS = ['other 1', 'other 2', 'other 3'] project = models.ForeignKey('Project', on_delete=models.CASCADE, verbose_name=_('project'), related_name='budget_items') label = models.ForeignKey( BudgetItemLabel, on_delete=models.CASCADE, verbose_name=_('budget item'), null=True, blank=True, help_text=_('Select the budget item(s) to indicate how the project budget is divided. ' 'Use the ‘Other’ fields to add custom budget items.') ) other_extra = ValidXMLCharField( max_length=30, null=True, blank=True, verbose_name=_('other label extra info'), help_text=_('Enter a description for an "other" budget item.'), ) # Translators: This is the amount of an budget item in a currency (€ or $) amount = models.DecimalField( _('budget item value'), max_digits=14, decimal_places=2, null=True, blank=True, help_text=_('Enter the amount of budget that is set aside for this specific budget item. ' 'Use a period to denote decimals.') ) # Extra IATI fields type = ValidXMLCharField( _('budget type'), blank=True, max_length=1, choices=codelist_choices(BUDGET_TYPE), help_text=_('Select whether this is an original or revised budget of the project.') ) period_start = models.DateField( _('budget item period start'), null=True, blank=True, help_text=_('Enter the start date (DD/MM/YYYY) for the budget period.') ) period_end = models.DateField( _('budget item period end'), null=True, blank=True, help_text=_('Enter the end date (DD/MM/YYYY) for the budget period.') ) value_date = models.DateField( _('budget item value date'), null=True, blank=True, help_text=_('Enter the date (DD/MM/YYYY) to be used for determining the exchange rate for ' 'currency conversions.') ) currency = ValidXMLCharField(_('currency'), max_length=3, blank=True, choices=codelist_choices(CURRENCY)) status = ValidXMLCharField( _('status'), max_length=1, blank=True, choices=codelist_choices(BUDGET_STATUS), help_text=_('The status explains whether the budget being reported is indicative or has ' 'been formally committed.')) def __str__(self): if self.label: if self.label.label == 'Other' and self.other_extra: budget_unicode = self.other_extra else: budget_unicode = self.label.label else: budget_unicode = '%s' % _('No budget item specified') if self.amount and self.currency: budget_unicode += ' - %s %s' % (str('{:,}'.format(int(self.amount))), self.currency) elif self.amount and not self.currency: budget_unicode += ' - %s %s' % (str('{:,}'.format(int(self.amount))), self.project.currency) else: budget_unicode += ' - %s' % _('No amount specified') if self.type == '2': budget_unicode += ' %s' % _('(Revised)') return budget_unicode
[docs] def clean(self): # Don't allow a start date before an end date if self.period_start and self.period_end and (self.period_start > self.period_end): raise ValidationError( {'period_start': '%s' % _('Period start cannot be at a later time than period ' 'end.'), 'period_end': '%s' % _('Period start cannot be at a later time than period ' 'end.')} )
[docs] def get_label(self): "Needed since we have to have a vanilla __str__() method for the admin" if self.label and self.label.label in self.OTHER_LABELS: # display "other" if other_extra is empty. # Translating here without translating the other labels seems corny return "other" if self.other_extra is None else self.other_extra.strip() elif self.label and self.label.label: return self.label.label else: return str(self)
[docs] def get_currency(self): if self.currency: return self.currency else: return self.project.currency
[docs] def iati_type(self): return codelist_value(BudgetType, self, 'type')
[docs] def iati_type_unicode(self): return str(self.iati_type())
[docs] def iati_currency(self): if self.currency: return codelist_value(Currency, self, 'currency') else: return codelist_value(Currency, self.project, 'currency')
[docs] def iati_currency_unicode(self): return str(self.iati_currency())
[docs] def iati_status(self): return codelist_value(BudgetStatus, self, 'status')
[docs] def iati_status_unicode(self): return str(self.iati_status())
class Meta: app_label = 'rsr' ordering = ('label',) verbose_name = _('budget item') verbose_name_plural = _('budget items') ordering = ('pk',)
[docs]class CountryBudgetItem(models.Model): project = models.ForeignKey('Project', on_delete=models.CASCADE, verbose_name=_('project'), related_name='country_budget_items') code = ValidXMLCharField( _('country budget item'), max_length=10, blank=True, choices=codelist_choices(BUDGET_IDENTIFIER), help_text=_('This item encodes the alignment of activities with both the functional and ' 'administrative classifications used in the recipient country’s Chart of ' 'Accounts. This applies to both on- and off-budget activities.') ) description = ValidXMLCharField( _('country budget item description'), max_length=100, blank=True, ) percentage = models.DecimalField( _('country budget item percentage'), blank=True, null=True, max_digits=4, decimal_places=1, validators=[MaxValueValidator(100), MinValueValidator(0)], help_text=_('If more than one identifier is reported, the percentage share must be ' 'reported and all percentages should add up to 100 percent. Use a period to ' 'denote decimals.') ) def __str__(self): return self.iati_code().name if self.code else '%s' % _('No code specified')
[docs] def iati_code(self): return codelist_value(BudgetIdentifier, self, 'code')
[docs] def iati_code_unicode(self): return str(self.iati_code())
class Meta: app_label = 'rsr' verbose_name = _('country budget item') verbose_name_plural = _('country budget items') ordering = ('pk',)