forked from beba/foerderbarometer
added proxies for declined & requested projects
This commit is contained in:
parent
4efab724ca
commit
7e4d197384
|
|
@ -11,6 +11,8 @@ from .models import (
|
||||||
Account,
|
Account,
|
||||||
Project,
|
Project,
|
||||||
ProjectCategory,
|
ProjectCategory,
|
||||||
|
ProjectRequest,
|
||||||
|
ProjectDeclined,
|
||||||
WikimediaProject,
|
WikimediaProject,
|
||||||
HonoraryCertificate,
|
HonoraryCertificate,
|
||||||
Library,
|
Library,
|
||||||
|
|
@ -126,6 +128,21 @@ class ProjectAdmin(admin.ModelAdmin):
|
||||||
class Media:
|
class Media:
|
||||||
js = ('dropdown/js/otrs_link.js',)
|
js = ('dropdown/js/otrs_link.js',)
|
||||||
|
|
||||||
|
granted = True
|
||||||
|
|
||||||
|
def get_queryset(self, request):
|
||||||
|
return super().get_queryset(request).filter(granted=self.granted)
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(ProjectRequest)
|
||||||
|
class ProjectRequestAdmin(ProjectAdmin):
|
||||||
|
granted = None
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(ProjectDeclined)
|
||||||
|
class ProjectDeclinedAdmin(ProjectAdmin):
|
||||||
|
granted = False
|
||||||
|
|
||||||
|
|
||||||
@admin.register(BusinessCard)
|
@admin.register(BusinessCard)
|
||||||
class BusinessCardAdmin(admin.ModelAdmin):
|
class BusinessCardAdmin(admin.ModelAdmin):
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,13 @@ class BaseApplicationForm(FdbForm):
|
||||||
settings.DATAPROTECTION, settings.FOERDERRICHTLINIEN))
|
settings.DATAPROTECTION, settings.FOERDERRICHTLINIEN))
|
||||||
|
|
||||||
|
|
||||||
|
PROJECT_COST_GT_1000_MESSAGE = format_html(
|
||||||
|
"""Bitte beachte, dass für Projektkosten über 1.000 € ein öffentlicher Projektplan erforderlich
|
||||||
|
ist (siehe <a href="{0}" target="blank_">Wikipedia:Förderung/Projektplanung)</a>.""",
|
||||||
|
'https://de.wikipedia.org/wiki/Wikipedia:F%C3%B6rderung/Projektplanung'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BaseProjectForm(ModelForm):
|
class BaseProjectForm(ModelForm):
|
||||||
categories = {
|
categories = {
|
||||||
'categories': ProjectCategory,
|
'categories': ProjectCategory,
|
||||||
|
|
@ -205,6 +212,14 @@ class ProjectForm(CommonOrderMixin, BaseProjectForm, BaseApplicationForm):
|
||||||
'all': ('css/dateFieldNoNowShortcutInTravels.css',)
|
'all': ('css/dateFieldNoNowShortcutInTravels.css',)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def clean_cost(self):
|
||||||
|
cost = self.cleaned_data['cost']
|
||||||
|
|
||||||
|
if cost > 1000:
|
||||||
|
raise forms.ValidationError(PROJECT_COST_GT_1000_MESSAGE, code='cost-gt-1000')
|
||||||
|
|
||||||
|
return cost
|
||||||
|
|
||||||
|
|
||||||
HOTEL_CHOICES = {
|
HOTEL_CHOICES = {
|
||||||
'TRUE': mark_safe('Hotelzimmer benötigt'),
|
'TRUE': mark_safe('Hotelzimmer benötigt'),
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ class Migration(migrations.Migration):
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='project',
|
model_name='project',
|
||||||
name='categories',
|
name='categories',
|
||||||
field=input.models.ProjectCategoryField(blank=True, related_name='projects', to='input.projectcategory', verbose_name='Projektkategorien'),
|
field=input.models.ProjectCategoryField(related_name='projects', to='input.projectcategory', verbose_name='Projektkategorien'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='project',
|
model_name='project',
|
||||||
|
|
@ -24,7 +24,7 @@ class Migration(migrations.Migration):
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='project',
|
model_name='project',
|
||||||
name='wikimedia_projects',
|
name='wikimedia_projects',
|
||||||
field=input.models.ProjectCategoryField(blank=True, related_name='projects', to='input.wikimediaproject', verbose_name='Wikimedia Projekte'),
|
field=input.models.ProjectCategoryField(related_name='projects', to='input.wikimediaproject', verbose_name='Wikimedia Projekte'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='project',
|
model_name='project',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Generated by Django 5.2.5 on 2025-10-16 13:15
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('input', '0101_wikimedia_project_categories_and_other'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ProjectDeclined',
|
||||||
|
fields=[
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Projekt (abgelehnt)',
|
||||||
|
'verbose_name_plural': 'Projekte (abgelehnt)',
|
||||||
|
'proxy': True,
|
||||||
|
'indexes': [],
|
||||||
|
'constraints': [],
|
||||||
|
},
|
||||||
|
bases=('input.project',),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ProjectRequest',
|
||||||
|
fields=[
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Projekt (beantragt)',
|
||||||
|
'verbose_name_plural': 'Projekte (beantragt)',
|
||||||
|
'proxy': True,
|
||||||
|
'indexes': [],
|
||||||
|
'constraints': [],
|
||||||
|
},
|
||||||
|
bases=('input.project',),
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='project',
|
||||||
|
options={'verbose_name': 'Projekt', 'verbose_name_plural': 'Projekte'},
|
||||||
|
),
|
||||||
|
]
|
||||||
141
input/models.py
141
input/models.py
|
|
@ -1,15 +1,12 @@
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from functools import partial
|
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.validators import MaxValueValidator
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.signals import pre_save
|
from django.db.models.signals import pre_save
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.forms import ModelMultipleChoiceField, CheckboxSelectMultiple
|
from django.forms import ModelMultipleChoiceField, CheckboxSelectMultiple
|
||||||
from django.forms.models import ModelChoiceIterator
|
|
||||||
from django.utils.functional import cached_property, classproperty
|
from django.utils.functional import cached_property, classproperty
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
@ -89,6 +86,10 @@ class Account(models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.code} {self.description}'
|
return f'{self.code} {self.description}'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def has_subaccounts(self):
|
||||||
|
return self.code == '21111'
|
||||||
|
|
||||||
|
|
||||||
class BaseProjectCategory(models.Model):
|
class BaseProjectCategory(models.Model):
|
||||||
OTHER: str
|
OTHER: str
|
||||||
|
|
@ -128,10 +129,10 @@ class WikimediaProject(BaseProjectCategory):
|
||||||
verbose_name_plural = 'Wikimedia Projekte'
|
verbose_name_plural = 'Wikimedia Projekte'
|
||||||
|
|
||||||
|
|
||||||
class ProductCategoryChoiceIterator(ModelChoiceIterator):
|
class ProductCategoryChoiceIterator(ModelMultipleChoiceField.iterator):
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
yield from ModelChoiceIterator.__iter__(self)
|
yield from ModelMultipleChoiceField.iterator.__iter__(self)
|
||||||
yield f'{self.field.other.id}', self.field.other.name
|
yield f'{self.field.other.id}', self.field.other.name
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -219,61 +220,62 @@ class Project(Volunteer):
|
||||||
project_of_year = models.IntegerField(default=0)
|
project_of_year = models.IntegerField(default=0)
|
||||||
end_quartal = models.CharField(max_length=15, null=True, blank=True, verbose_name='Quartal Projekt Ende')
|
end_quartal = models.CharField(max_length=15, null=True, blank=True, verbose_name='Quartal Projekt Ende')
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
class Meta:
|
||||||
|
verbose_name = 'Projekt'
|
||||||
|
verbose_name_plural = 'Projekte'
|
||||||
|
|
||||||
generate_finance_id = False
|
def __str__(self):
|
||||||
|
return f'{self.pid or self.id} {self.name}'
|
||||||
|
|
||||||
'''we generate the autogenerated fields here'''
|
def save(self, *, using=None, **kwargs):
|
||||||
# we don't call save with args/kwargs to avoid UNIQUE CONSTRAINT errors
|
kwargs['using'] = using
|
||||||
# but maybe there is a better solution?
|
|
||||||
|
|
||||||
if not self.pk:
|
|
||||||
print('NO PK THERE')
|
|
||||||
generate_finance_id = True
|
|
||||||
super().save()
|
|
||||||
else:
|
|
||||||
orig = type(self).objects.get(pk=self.pk) # Originaldaten aus der DB abrufen
|
|
||||||
if orig.start.year != self.start.year:
|
|
||||||
generate_finance_id = True
|
|
||||||
if orig.account.code != self.account.code:
|
|
||||||
if str(self.account.code) == '21111':
|
|
||||||
generate_finance_id = True
|
|
||||||
else:
|
|
||||||
self.finance_id = str(self.account.code)
|
|
||||||
|
|
||||||
if generate_finance_id:
|
|
||||||
print('MUST GENERATE FINANCE ID')
|
|
||||||
year = self.start.year
|
|
||||||
projects = Project.objects.filter(start__year=year)
|
|
||||||
if not projects:
|
|
||||||
self.project_of_year = 1
|
|
||||||
# self.pid = str(self.start.year) + '-' + str(self.account.code) + str(self.project_of_year).zfill(3)
|
|
||||||
else:
|
|
||||||
# get the project of year number of latest entry
|
|
||||||
projects = projects.order_by('-project_of_year')[0]
|
|
||||||
# add one to value of latest entry
|
|
||||||
self.project_of_year = int(projects.project_of_year) + 1
|
|
||||||
# self.pid = str(self.start.year) + '-' + str(self.account.code) + str(self.project_of_year).zfill(3)
|
|
||||||
|
|
||||||
if str(self.account.code) == '21111':
|
|
||||||
self.finance_id = str(self.account.code) + '-' + str(self.project_of_year).zfill(3)
|
|
||||||
else:
|
|
||||||
self.finance_id = str(self.account.code)
|
|
||||||
|
|
||||||
# print (('Current PID',self.pid))
|
|
||||||
|
|
||||||
if not self.pid:
|
|
||||||
self.pid = str(self.account.code) + str(self.pk).zfill(8)
|
|
||||||
# self.pid = str(self.account.code) + str(self.pk).zfill(3)
|
|
||||||
print(('Hallo Leute! Ich save jetzt mal MIT PID DANN!!!', self.pid))
|
|
||||||
|
|
||||||
if self.end:
|
if self.end:
|
||||||
self.end_quartal = f'Q{self.end.month // 4 + 1}'
|
self.end_quartal = f'Q{self.end.month // 4 + 1}'
|
||||||
|
else:
|
||||||
|
self.end_quartal = ''
|
||||||
|
|
||||||
super().save()
|
if not self.account:
|
||||||
|
self.finance_id = ''
|
||||||
|
self.project_of_year = 0
|
||||||
|
|
||||||
def __str__(self):
|
return super().save(**kwargs)
|
||||||
return f'{self.pid} {self.name}'
|
|
||||||
|
if self.should_generate_finance_id():
|
||||||
|
self.generate_finance_id()
|
||||||
|
|
||||||
|
super().save(**kwargs)
|
||||||
|
|
||||||
|
if not self.pid:
|
||||||
|
self.pid = f'{self.account.code}-{self.id:08d}'
|
||||||
|
super().save(update_fields=['pid'], using=using)
|
||||||
|
|
||||||
|
def should_generate_finance_id(self):
|
||||||
|
if self.id is None:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if not self.finance_id:
|
||||||
|
return True
|
||||||
|
|
||||||
|
start, account_id = type(self).objects.values_list('start', 'account').get(id=self.id)
|
||||||
|
|
||||||
|
return not (self.start.year == start.year and self.account_id == account_id)
|
||||||
|
|
||||||
|
def generate_finance_id(self):
|
||||||
|
"""
|
||||||
|
This is an improved version of the old code for generating a finance id.
|
||||||
|
There is still no protection by constraints against duplicate finance ids!
|
||||||
|
"""
|
||||||
|
|
||||||
|
queryset = Project.objects.exclude(id=self.id).filter(start__year=self.start.year)
|
||||||
|
max_project_of_year = queryset.aggregate(max=models.Max('project_of_year')).get('max') or 0
|
||||||
|
|
||||||
|
self.project_of_year = project_of_year = max_project_of_year + 1
|
||||||
|
|
||||||
|
if self.account.has_subaccounts:
|
||||||
|
self.finance_id = f'{self.account.code}-{project_of_year:03d}'
|
||||||
|
else:
|
||||||
|
self.finance_id = self.account.code
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if (self.start and self.end) and (self.end < self.start):
|
if (self.start and self.end) and (self.end < self.start):
|
||||||
|
|
@ -284,6 +286,22 @@ class Project(Volunteer):
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectRequest(Project):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
proxy = True
|
||||||
|
verbose_name = 'Projekt (beantragt)'
|
||||||
|
verbose_name_plural = 'Projekte (beantragt)'
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectDeclined(Project):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
proxy = True
|
||||||
|
verbose_name = 'Projekt (abgelehnt)'
|
||||||
|
verbose_name_plural = 'Projekte (abgelehnt)'
|
||||||
|
|
||||||
|
|
||||||
class Intern(Volunteer):
|
class Intern(Volunteer):
|
||||||
'''abstract base class for data entry from /intern (except Project)'''
|
'''abstract base class for data entry from /intern (except Project)'''
|
||||||
request_url = models.URLField(max_length=2000, verbose_name='Antrag (URL)')
|
request_url = models.URLField(max_length=2000, verbose_name='Antrag (URL)')
|
||||||
|
|
@ -304,7 +322,7 @@ class HonoraryCertificate(Intern):
|
||||||
project = models.ForeignKey(Project, null=True, blank=True, on_delete=models.SET_NULL)
|
project = models.ForeignKey(Project, null=True, blank=True, on_delete=models.SET_NULL)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'Certificate for ' + self.realname
|
return f'Certificate for {self.realname}'
|
||||||
|
|
||||||
|
|
||||||
TRANSPORT_CHOICES = {
|
TRANSPORT_CHOICES = {
|
||||||
|
|
@ -561,21 +579,6 @@ class BusinessCard(TermsConsentMixin, Extern):
|
||||||
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name='interne Anmerkungen')
|
intern_notes = models.TextField(max_length=1000, blank=True, verbose_name='interne Anmerkungen')
|
||||||
|
|
||||||
|
|
||||||
class Decision(models.TextChoices):
|
|
||||||
OPEN = 'OPEN', 'offen'
|
|
||||||
APPROVED = 'APPROVED', 'bewilligt'
|
|
||||||
DECLINED = 'DECLINED', 'abgelehnt'
|
|
||||||
|
|
||||||
|
|
||||||
validate_cost = MaxValueValidator(
|
|
||||||
limit_value=100,
|
|
||||||
message=(
|
|
||||||
'Bitte beachte, dass für Projektkosten über 1.000 EUR '
|
|
||||||
'ein öffentlicher Projektplan erforderlich ist '
|
|
||||||
'(siehe Wikipedia:Förderung/Projektplanung).'
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
MODELS = {
|
MODELS = {
|
||||||
TYPE_BIB: Library,
|
TYPE_BIB: Library,
|
||||||
TYPE_ELIT: ELiterature,
|
TYPE_ELIT: ELiterature,
|
||||||
|
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
from datetime import date
|
|
||||||
from django.db import transaction
|
|
||||||
from .models import ProjectRequest, ProjectsDeclined, Project, Account
|
|
||||||
|
|
||||||
def approve_project_request(request_id: int, decided_by: str, account_code: str) -> Project:
|
|
||||||
# Use a DB transaction so either all changes commit, or none (atomic workflow).
|
|
||||||
with transaction.atomic():
|
|
||||||
# SELECT ... FOR UPDATE: lock the row to avoid concurrent approvals/declines.
|
|
||||||
req = ProjectRequest.objects.select_for_update().get(pk=request_id)
|
|
||||||
|
|
||||||
# Mark the request as approved and persist the decision metadata.
|
|
||||||
req.decision = 'APPROVED'
|
|
||||||
req.decision_date = date.today() # For DateField a date is fine; prefer timezone.localdate() if TZ-sensitive.
|
|
||||||
req.decided_by = decided_by
|
|
||||||
req.save()
|
|
||||||
|
|
||||||
# The Account (Kostenstelle) must be assigned by WMDE in the admin workflow.
|
|
||||||
# .get() will raise DoesNotExist/MultipleObjectsReturned if data integrity is broken.
|
|
||||||
account = Account.objects.get(code=account_code)
|
|
||||||
|
|
||||||
# Create the actual Project from the request data.
|
|
||||||
# Project.save() will generate pid/finance_id/end_quartal according to your model logic.
|
|
||||||
proj = Project.objects.create(
|
|
||||||
realname=req.realname,
|
|
||||||
email=req.email,
|
|
||||||
end_mail_send=False,
|
|
||||||
name=req.name,
|
|
||||||
description=req.description,
|
|
||||||
start=req.start,
|
|
||||||
end=req.end,
|
|
||||||
page=req.page,
|
|
||||||
group=req.group,
|
|
||||||
location=req.location,
|
|
||||||
participants_estimated=req.participants_estimated,
|
|
||||||
insurance=req.insurance,
|
|
||||||
cost=req.cost,
|
|
||||||
account=account,
|
|
||||||
notes=req.notes,
|
|
||||||
granted=True,
|
|
||||||
granted_date=date.today(), # Consider timezone.localdate() if you care about time zones.
|
|
||||||
granted_from=decided_by,
|
|
||||||
)
|
|
||||||
|
|
||||||
# After successful creation we remove the original request (it has been fulfilled).
|
|
||||||
# Because we're inside an atomic block, both operations succeed/fail together.
|
|
||||||
req.delete()
|
|
||||||
|
|
||||||
# Return the created project for further processing in the caller if needed.
|
|
||||||
return proj
|
|
||||||
|
|
||||||
|
|
||||||
def decline_project_request(request_id: int, reason: str | None = None):
|
|
||||||
# Same transactional guarantees for declines.
|
|
||||||
with transaction.atomic():
|
|
||||||
# Lock the row to prevent concurrent decisions.
|
|
||||||
req = ProjectRequest.objects.select_for_update().get(pk=request_id)
|
|
||||||
|
|
||||||
# Mark as declined and persist decision date.
|
|
||||||
req.decision = 'DECLINED'
|
|
||||||
req.decision_date = date.today()
|
|
||||||
req.save()
|
|
||||||
|
|
||||||
# Archive minimal relevant information in a dedicated table.
|
|
||||||
# No pid/finance_id should be created for declined items.
|
|
||||||
ProjectsDeclined.objects.create(
|
|
||||||
original_request_id=req.id,
|
|
||||||
name=req.name,
|
|
||||||
realname=req.realname,
|
|
||||||
email=req.email,
|
|
||||||
decision_date=req.decision_date,
|
|
||||||
reason=reason or '',
|
|
||||||
)
|
|
||||||
|
|
||||||
# Remove the original request after archiving.
|
|
||||||
req.delete()
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from unittest import skip
|
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
|
|
@ -8,8 +7,12 @@ from input.models import HonoraryCertificate, Project, Account, Literature
|
||||||
|
|
||||||
class ModelTestCase(TestCase):
|
class ModelTestCase(TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
cls.account = Account.objects.create(code='1234', description='blabla')
|
||||||
|
|
||||||
def test_set_granted(self):
|
def test_set_granted(self):
|
||||||
'''test if the model function set_granted() works as intended'''
|
""" test if the model function set_granted() works as intended """
|
||||||
obj = HonoraryCertificate.objects.create(realname='hurzel', email='hurzel@web.de')
|
obj = HonoraryCertificate.objects.create(realname='hurzel', email='hurzel@web.de')
|
||||||
self.assertEqual(obj.granted, None)
|
self.assertEqual(obj.granted, None)
|
||||||
HonoraryCertificate.set_granted(obj.pk, True)
|
HonoraryCertificate.set_granted(obj.pk, True)
|
||||||
|
|
@ -17,11 +20,8 @@ class ModelTestCase(TestCase):
|
||||||
self.assertEqual(obj2.granted, True)
|
self.assertEqual(obj2.granted, True)
|
||||||
|
|
||||||
def test_project_of_year(self):
|
def test_project_of_year(self):
|
||||||
''' test if the finance id is resettet ad start of year'''
|
""" test if the finance id is resettet ad start of year """
|
||||||
acc = Account.objects.create()
|
acc = self.account
|
||||||
acc.code='1234'
|
|
||||||
acc.description='blabla'
|
|
||||||
acc.save()
|
|
||||||
startdate = date(2022, 1, 1)
|
startdate = date(2022, 1, 1)
|
||||||
obj = Project.objects.create(account=acc, name='testproject', start=startdate)
|
obj = Project.objects.create(account=acc, name='testproject', start=startdate)
|
||||||
self.assertEqual(obj.project_of_year, 1)
|
self.assertEqual(obj.project_of_year, 1)
|
||||||
|
|
@ -35,39 +35,54 @@ class ModelTestCase(TestCase):
|
||||||
obj3 = Project.objects.create(account=acc, name='testproject2', start=startdate)
|
obj3 = Project.objects.create(account=acc, name='testproject2', start=startdate)
|
||||||
self.assertEqual(obj3.project_of_year, 3)
|
self.assertEqual(obj3.project_of_year, 3)
|
||||||
|
|
||||||
@skip('Finance ID generation has been changed and this test has not been adapted accordingly.')
|
|
||||||
def test_finance_id(self):
|
def test_finance_id(self):
|
||||||
''' test if the finance counting is correct'''
|
""" test if the finance counting is correct """
|
||||||
acc = Account.objects.create(code='1234', description='blabla')
|
acc = self.account
|
||||||
startdate = date(2022, 1, 1)
|
startdate = date(2022, 1, 1)
|
||||||
obj = Project.objects.create(account=acc, name='testproject', start=startdate)
|
obj = Project.objects.create(account=acc, name='testproject', start=startdate)
|
||||||
self.assertEqual(obj.finance_id,"1234001")
|
self.assertEqual(obj.finance_id, "1234")
|
||||||
|
|
||||||
obj2 = Project.objects.create(account=acc, name='testproject2', start=startdate)
|
obj2 = Project.objects.create(account=acc, name='testproject2', start=startdate)
|
||||||
self.assertEqual(obj2.finance_id,"1234002")
|
self.assertEqual(obj2.finance_id, "1234")
|
||||||
|
|
||||||
olddate = date(2021, 12, 31)
|
olddate = date(2021, 12, 31)
|
||||||
obj4 = Project.objects.create(account=acc, name='testproject2', start=olddate)
|
obj4 = Project.objects.create(account=acc, name='testproject2', start=olddate)
|
||||||
|
|
||||||
obj3 = Project.objects.create(account=acc, name='testproject2', start=startdate)
|
obj3 = Project.objects.create(account=acc, name='testproject2', start=startdate)
|
||||||
self.assertEqual(obj3.finance_id,"1234003")
|
self.assertEqual(obj3.finance_id, "1234")
|
||||||
|
|
||||||
# def test_pid(self):
|
def test_financed_id_for_subaccounts(self):
|
||||||
# ''' test if the pid counting is correct '''
|
account = Account.objects.create(code='21111', description='has subaccounts')
|
||||||
# acc = Account.objects.create(code='1234', description='blabla')
|
obj = Project.objects.create(account=account, name='test', start=date(2025, 1, 1))
|
||||||
# startdate = date(2022,1,1)
|
|
||||||
# obj = Project.objects.create(account= acc, name='testproject', start=startdate)
|
self.assertEqual(obj.finance_id, f'{account.code}-001')
|
||||||
# self.assertEqual(obj.pid,"1234001")
|
|
||||||
# self.assertEqual(obj.account.code,"1234")
|
def test_finance_id_later(self):
|
||||||
#
|
obj = Project.objects.create(name='test', start=date(2025, 1, 1))
|
||||||
# obj2 = Project.objects.create(account= acc, name='testproject2', start=startdate)
|
|
||||||
# self.assertEqual(obj2.pid,"1234002")
|
self.assertFalse(obj.finance_id)
|
||||||
#
|
|
||||||
# olddate = date(2021,12,31)
|
obj.account = self.account
|
||||||
# obj4 = Project.objects.create(account= acc, name='testproject2', start=olddate)
|
obj.save()
|
||||||
#
|
|
||||||
# obj3 = Project.objects.create(account= acc, name='testproject2', start=startdate)
|
self.assertTrue(obj.finance_id)
|
||||||
# self.assertEqual(obj3.pid,"1234004")
|
|
||||||
|
def test_pid(self):
|
||||||
|
""" test if the pid counting is correct """
|
||||||
|
acc = self.account
|
||||||
|
startdate = date(2022, 1, 1)
|
||||||
|
obj = Project.objects.create(account=acc, name='testproject', start=startdate)
|
||||||
|
self.assertEqual(obj.pid, "1234-00000001")
|
||||||
|
self.assertEqual(obj.account.code, "1234")
|
||||||
|
|
||||||
|
obj2 = Project.objects.create(account=acc, name='testproject2', start=startdate)
|
||||||
|
self.assertEqual(obj2.pid, "1234-00000002")
|
||||||
|
|
||||||
|
olddate = date(2021, 12, 31)
|
||||||
|
obj4 = Project.objects.create(account=acc, name='testproject2', start=olddate)
|
||||||
|
|
||||||
|
obj3 = Project.objects.create(account=acc, name='testproject2', start=startdate)
|
||||||
|
self.assertEqual(obj3.pid, "1234-00000004")
|
||||||
|
|
||||||
def test_literature(self):
|
def test_literature(self):
|
||||||
obj = Literature.objects.create(cost='100', notes='jolo', selfbuy_give_data=False)
|
obj = Literature.objects.create(cost='100', notes='jolo', selfbuy_give_data=False)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue