├── .gitignore ├── .travis.yml ├── AUTHORS ├── CHANGELOG.txt ├── DESCRIPTION ├── LICENSE ├── MANIFEST.in ├── README.rst ├── manage.py ├── payslip ├── __init__.py ├── admin.py ├── app_settings.py ├── fixtures │ └── bootstrap_auth.json ├── forms.py ├── migrations │ ├── 0001_initial.py │ └── __init__.py ├── models.py ├── south_migrations │ ├── 0001_initial.py │ ├── 0002_auto__add_field_employee_is_manager.py │ ├── 0003_auto__add_field_extrafieldtype_model__add_field_extrafieldtype_fixed_v.py │ ├── 0004_auto__add_field_paymenttype_rrule__chg_field_extrafieldtype_model.py │ ├── 0005_auto__add_field_payment_end_date.py │ └── __init__.py ├── static │ └── payslip │ │ ├── css │ │ ├── base.css │ │ └── payslip.css │ │ └── js │ │ └── payslip.js ├── templates │ └── payslip │ │ ├── company_confirm_delete.html │ │ ├── company_form.html │ │ ├── dashboard.html │ │ ├── delete_base.html │ │ ├── employee_confirm_delete.html │ │ ├── employee_form.html │ │ ├── extrafield_confirm_delete.html │ │ ├── extrafield_form.html │ │ ├── extrafieldtype_confirm_delete.html │ │ ├── extrafieldtype_form.html │ │ ├── payment_confirm_delete.html │ │ ├── payment_form.html │ │ ├── paymenttype_confirm_delete.html │ │ ├── paymenttype_form.html │ │ ├── payslip.html │ │ ├── payslip_base.html │ │ └── payslip_form.html ├── templatetags │ ├── __init__.py │ └── payslip_tags.py ├── tests │ ├── __init__.py │ ├── forms_tests.py │ ├── models_tests.py │ ├── settings.py │ ├── tag_tests.py │ ├── test_app │ │ ├── __init__.py │ │ ├── models.py │ │ └── templates │ │ │ ├── 404.html │ │ │ └── 500.html │ ├── test_settings.py │ ├── urls.py │ └── views_tests.py ├── urls.py └── views.py ├── requirements.txt ├── runtests.py ├── setup.py ├── test_requirements.txt └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info/ 2 | *.pyc 3 | .tox 4 | .coverage 5 | coverage/ 6 | db.sqlite 7 | dist/ 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.6" 4 | - "2.7" 5 | install: pip install -r requirements.txt --use-mirrors 6 | script: python payslip/tests/runtests.py 7 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Current or previous core committers 2 | 3 | Tobias Lorenz 4 | 5 | Contributors (in alphabetical order) 6 | 7 | * Your name could stand here :) 8 | -------------------------------------------------------------------------------- /CHANGELOG.txt: -------------------------------------------------------------------------------- 1 | === 0.3.X (ongoing) === 2 | 3 | - Added proper pdf filename 4 | - Prepared app for Django 1.9 and Python 3.5 5 | - Upgrade to Django>=1.8 6 | 7 | === 0.2 === 8 | 9 | - Improved dashboard template 10 | - Improved payslip form 11 | - Fixed payment querysets 12 | - Changed to weasyprint 13 | 14 | === 0.2 === 15 | 16 | - Made use of timezones 17 | - updated requirements and templates 18 | - Fixed single payment generator 19 | 20 | === 0.1 === 21 | 22 | Initial commit 23 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | A reusable Django app that allows you to enter salary data of your employees 2 | so that authorized persons can view, print and export (PDF) their payslips. 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2012 Martin Brochhaus 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS 2 | include LICENSE 3 | include DESCRIPTION 4 | include CHANGELOG.txt 5 | include README.md 6 | graft payslip 7 | global-exclude *.orig *.pyc *.log 8 | prune payslip/tests/coverage/ 9 | prune payslip/.ropeproject/ 10 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Django Payslip 2 | ============== 3 | 4 | A reusable Django app that allows you to enter salary data of your employees 5 | so that authorized persons can view, print and export (PDF) their payslips. 6 | 7 | Installation 8 | ------------ 9 | 10 | You need to install the following prerequisites in order to use this app:: 11 | 12 | pip install Django 13 | pip install django-libs 14 | pip install python-dateutil 15 | pip install WeasyPrint 16 | 17 | If you want to install the latest stable release from PyPi:: 18 | 19 | $ pip install django-payslip 20 | 21 | If you feel adventurous and want to install the latest commit from GitHub:: 22 | 23 | $ pip install -e git://github.com/bitmazk/django-payslip.git#egg=payslip 24 | 25 | Add ``payslip`` to your ``INSTALLED_APPS``:: 26 | 27 | INSTALLED_APPS = ( 28 | ..., 29 | 'payslip', 30 | ) 31 | 32 | Hook this app into your ``urls.py``:: 33 | 34 | urlpatterns = patterns('', 35 | ... 36 | url(r'^payslip/', include('payslip.urls')), 37 | ) 38 | 39 | 40 | Features & Usage 41 | ---------------- 42 | 43 | You can: 44 | 45 | * Define companies 46 | * Assign employees (a user model is created automatically) 47 | * Define payment types 48 | * Create payments 49 | * Create custom extra fields for COMPANIES, EMPLOYEES and PAYMENTS 50 | * Create global attributes for those custom fields (dropdown fields) 51 | * Generate custom payslips 52 | * Print those payslips or export them as styled PDF documents 53 | 54 | There's already a print-ready template for your payslips, which should cover 55 | mainly used payslips. If you want to you can override the template with your 56 | own styles. Find it here ``payslip/templates/payslip/payslip.html``. 57 | 58 | You can also create your own CSS, but be sure to cover print styles. Find it 59 | here ``static/payslip/css/payslip.css``. 60 | 61 | After you have added the basic company information needed in your template, you 62 | can add payments and employees and start paysliping. :) Have fun with it. 63 | 64 | 65 | Settings 66 | -------- 67 | 68 | PAYSLIP_CURRENCY 69 | ++++++++++++++++ 70 | 71 | Default: 'EUR' 72 | 73 | Your preferred currency acronym. 74 | 75 | 76 | Contribute 77 | ---------- 78 | 79 | If you want to contribute to this project, please perform the following steps:: 80 | 81 | # Fork this repository 82 | # Clone your fork 83 | $ mkvirtualenv -p python2.7 payslip 84 | $ pip install -r requirements.txt 85 | $ ./payslip/tests/runtests.sh 86 | # You should get no failing tests 87 | 88 | $ git co -b feature_branch master 89 | # Implement your feature and tests 90 | # Describe your change in the CHANGELOG.txt 91 | $ git add . && git commit 92 | $ git push origin feature_branch 93 | # Send us a pull request for your feature branch 94 | 95 | Whenever you run the tests a coverage output will be generated in 96 | ``tests/coverage/index.html``. When adding new features, please make sure that 97 | you keep the coverage at 100%. 98 | 99 | If you are making changes that need to be tested in a browser (i.e. to the 100 | CSS or JS files), you might want to setup a Django project, follow the 101 | installation instructions above, then run ``python setup.py develop``. This 102 | will just place an egg-link to your cloned fork in your project's virtualenv. 103 | 104 | Roadmap 105 | ------- 106 | 107 | * Add employee and manager dashboards 108 | 109 | Check the issue tracker on github for further milestones and features to come. 110 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", 7 | "payslip.tests.settings") 8 | 9 | from django.core.management import execute_from_command_line 10 | 11 | execute_from_command_line(sys.argv) 12 | -------------------------------------------------------------------------------- /payslip/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __version__ = '0.3.2' 3 | -------------------------------------------------------------------------------- /payslip/admin.py: -------------------------------------------------------------------------------- 1 | """Admin classes for the payslip app.""" 2 | from django.contrib import admin 3 | 4 | from . import models 5 | 6 | 7 | admin.site.register(models.Company) 8 | admin.site.register(models.Employee) 9 | admin.site.register(models.ExtraField) 10 | admin.site.register(models.ExtraFieldType) 11 | admin.site.register(models.Payment) 12 | admin.site.register(models.PaymentType) 13 | -------------------------------------------------------------------------------- /payslip/app_settings.py: -------------------------------------------------------------------------------- 1 | """Settings of the ``payslip``` application.""" 2 | from django.conf import settings 3 | 4 | CURRENCY = getattr(settings, 'PAYSLIP_CURRENCY', 'EUR') 5 | -------------------------------------------------------------------------------- /payslip/fixtures/bootstrap_auth.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pk": 1, 4 | "model": "auth.user", 5 | "fields": { 6 | "username": "admin", 7 | "first_name": "", 8 | "last_name": "", 9 | "is_active": true, 10 | "is_superuser": true, 11 | "is_staff": true, 12 | "last_login": "2012-11-26T17:04:43", 13 | "groups": [], 14 | "user_permissions": [], 15 | "password": "md5$iL4a3g0DV1Et$7ee95f30b9a4946ed09cca43505b2709", 16 | "email": "info@example.com", 17 | "date_joined": "2012-06-16T10:38:31" 18 | } 19 | } 20 | ] -------------------------------------------------------------------------------- /payslip/forms.py: -------------------------------------------------------------------------------- 1 | """Forms for the ``payslip`` app.""" 2 | import hashlib 3 | 4 | from django.contrib.auth import get_user_model 5 | from django.contrib.auth.hashers import make_password 6 | from django.db.models import Q 7 | from django import forms 8 | from django.utils import timezone 9 | from django.utils.translation import ugettext_lazy as _ 10 | 11 | from dateutil.relativedelta import relativedelta 12 | 13 | from .models import ( 14 | Company, 15 | Employee, 16 | ExtraField, 17 | ExtraFieldType, 18 | Payment, 19 | ) 20 | 21 | 22 | def get_md5_hexdigest(email): 23 | """ 24 | Returns an md5 hash for a given email. 25 | 26 | The length is 30 so that it fits into Django's ``User.username`` field. 27 | 28 | """ 29 | h = hashlib.new('md5') 30 | h.update(email.encode('utf-8')) 31 | return h.hexdigest()[0:30] 32 | 33 | 34 | def generate_username(email): 35 | """ 36 | Generates a unique username for the given email. 37 | 38 | The username will be an md5 hash of the given email. If the username exists 39 | we just append `a` to the email until we get a unique md5 hash. 40 | 41 | """ 42 | username = get_md5_hexdigest(email) 43 | found_unique_username = False 44 | while not found_unique_username: 45 | try: 46 | get_user_model().objects.get(username=username) 47 | email = '{0}a'.format(email) 48 | username = get_md5_hexdigest(email) 49 | except get_user_model().DoesNotExist: 50 | found_unique_username = True 51 | return username 52 | 53 | 54 | class ExtraFieldFormMixin(object): 55 | """Mixin to handle extra field related functions.""" 56 | def __init__(self, *args, **kwargs): 57 | self.extra_field_types = ExtraFieldType.objects.filter( 58 | Q(model=self.Meta.model.__name__) | Q(model__isnull=True)) 59 | if kwargs.get('instance'): 60 | for extra_field_type in self.extra_field_types: 61 | try: 62 | field = kwargs.get('instance').extra_fields.get( 63 | field_type__name=extra_field_type.name) 64 | except ExtraField.DoesNotExist: 65 | pass 66 | else: 67 | kwargs['initial'].update({'{0}'.format( 68 | extra_field_type.name): field.value}) 69 | super(ExtraFieldFormMixin, self).__init__(*args, **kwargs) 70 | for extra_field_type in self.extra_field_types: 71 | if extra_field_type.fixed_values: 72 | choices = [(x.value, x.value) 73 | for x in extra_field_type.extra_fields.all()] 74 | choices.append(('', '-----')) 75 | self.fields[extra_field_type.name] = forms.ChoiceField( 76 | required=False, 77 | choices=list(set(choices)), 78 | ) 79 | else: 80 | self.fields[extra_field_type.name] = forms.CharField( 81 | required=False, max_length=200) 82 | 83 | def save(self, *args, **kwargs): 84 | resp = super(ExtraFieldFormMixin, self).save(*args, **kwargs) 85 | for extra_field_type in self.extra_field_types: 86 | try: 87 | field_to_save = self.instance.extra_fields.get( 88 | field_type__name=extra_field_type.name) 89 | except ExtraField.DoesNotExist: 90 | field_to_save = None 91 | if extra_field_type.fixed_values: 92 | if field_to_save: 93 | self.instance.extra_fields.remove( 94 | self.instance.extra_fields.get( 95 | field_type__name=extra_field_type.name)) 96 | try: 97 | field_to_save = ExtraField.objects.get( 98 | field_type__name=extra_field_type.name, 99 | value=self.data.get(extra_field_type.name)) 100 | except ExtraField.DoesNotExist: 101 | pass 102 | else: 103 | self.instance.extra_fields.add(field_to_save) 104 | else: 105 | if field_to_save and self.data.get(extra_field_type.name): 106 | field_to_save.value = self.data[extra_field_type.name] 107 | field_to_save.save() 108 | elif self.data.get(extra_field_type.name): 109 | new_field = ExtraField( 110 | field_type=extra_field_type, 111 | value=self.data.get(extra_field_type.name), 112 | ) 113 | new_field.save() 114 | self.instance.extra_fields.add(new_field) 115 | return resp 116 | 117 | 118 | class EmployeeForm(ExtraFieldFormMixin, forms.ModelForm): 119 | """Form to create a new Employee instance.""" 120 | first_name = forms.CharField(max_length=30) 121 | last_name = forms.CharField(max_length=30) 122 | email = forms.EmailField() 123 | password = forms.CharField(widget=forms.PasswordInput(), max_length=128) 124 | retype_password = forms.CharField(widget=forms.PasswordInput(), 125 | max_length=128) 126 | 127 | def __init__(self, company, *args, **kwargs): 128 | self.company = company 129 | if kwargs.get('instance'): 130 | instance = kwargs.get('instance') 131 | user = instance.user 132 | kwargs['initial'] = { 133 | 'first_name': user.first_name, 134 | 'last_name': user.last_name, 135 | 'email': user.email, 136 | } 137 | super(EmployeeForm, self).__init__(*args, **kwargs) 138 | if self.instance.id: 139 | del self.fields['password'] 140 | del self.fields['retype_password'] 141 | if self.company and self.company.pk: 142 | del self.fields['company'] 143 | 144 | def clean_email(self): 145 | """ 146 | Validate that the username is alphanumeric and is not already 147 | in use. 148 | 149 | """ 150 | email = self.cleaned_data['email'] 151 | try: 152 | user = get_user_model().objects.get(email__iexact=email) 153 | except get_user_model().DoesNotExist: 154 | return email 155 | if self.instance.id and user == self.instance.user: 156 | return email 157 | raise forms.ValidationError( 158 | _('A user with that email already exists.')) 159 | 160 | def clean(self): 161 | """ 162 | Verifiy that the values entered into the two password fields match. 163 | 164 | Note that an error here will end up in ``non_field_errors()`` because 165 | it doesn't apply to a single field. 166 | 167 | """ 168 | data = self.cleaned_data 169 | if 'email' not in data: 170 | return data 171 | if ('password' in data and 'retype_password' in data): 172 | if data['password'] != data['retype_password']: 173 | raise forms.ValidationError( 174 | _("The two password fields didn't match.")) 175 | 176 | self.cleaned_data['username'] = generate_username(data['email']) 177 | return self.cleaned_data 178 | 179 | def save(self, *args, **kwargs): 180 | if self.instance.id: 181 | get_user_model().objects.filter(pk=self.instance.user.pk).update( 182 | first_name=self.cleaned_data.get('first_name'), 183 | last_name=self.cleaned_data.get('last_name'), 184 | email=self.cleaned_data.get('email'), 185 | ) 186 | else: 187 | user = get_user_model().objects.create( 188 | username=self.cleaned_data.get('email'), 189 | first_name=self.cleaned_data.get('first_name'), 190 | last_name=self.cleaned_data.get('last_name'), 191 | email=self.cleaned_data.get('email'), 192 | password=make_password(self.cleaned_data.get('password')), 193 | ) 194 | self.instance.user = user 195 | if self.company and self.company.pk: 196 | self.instance.company = Company.objects.get(pk=self.company.pk) 197 | return super(EmployeeForm, self).save(*args, **kwargs) 198 | 199 | class Meta: 200 | model = Employee 201 | fields = ('company', 'hr_number', 'address', 'title', 'is_manager') 202 | 203 | 204 | class PaymentForm(ExtraFieldFormMixin, forms.ModelForm): 205 | """Form to create a new Payment instance.""" 206 | class Meta: 207 | model = Payment 208 | fields = ('payment_type', 'employee', 'amount', 'date', 'end_date', 209 | 'description') 210 | 211 | 212 | class ExtraFieldForm(forms.ModelForm): 213 | """Form to create a new ExtraField instance.""" 214 | def __init__(self, *args, **kwargs): 215 | super(ExtraFieldForm, self).__init__(*args, **kwargs) 216 | self.fields['field_type'].queryset = ExtraFieldType.objects.filter( 217 | fixed_values=True) 218 | 219 | class Meta: 220 | model = ExtraField 221 | fields = '__all__' 222 | 223 | 224 | class PayslipForm(forms.Form): 225 | """Form to create a custom payslip.""" 226 | year = forms.ChoiceField() 227 | month = forms.ChoiceField() 228 | employee = forms.ChoiceField() 229 | 230 | def __init__(self, company, *args, **kwargs): 231 | super(PayslipForm, self).__init__(*args, **kwargs) 232 | last_month = timezone.now().replace(day=1) - relativedelta(months=1) 233 | self.fields['month'].choices = ( 234 | (1, _('January')), 235 | (2, _('February')), 236 | (3, _('March')), 237 | (4, _('April')), 238 | (5, _('May')), 239 | (6, _('June')), 240 | (7, _('July')), 241 | (8, _('August')), 242 | (9, _('September')), 243 | (10, _('October')), 244 | (11, _('November')), 245 | (12, _('December')), 246 | ) 247 | self.fields['month'].initial = last_month.month 248 | current_year = timezone.now().year 249 | self.fields['year'].choices = [ 250 | (current_year - x, current_year - x) for x in range(0, 20)] 251 | self.fields['year'].initial = last_month.year 252 | self.company = company 253 | if self.company: 254 | self.fields['employee'].choices = [( 255 | x.id, x) for x in self.company.employees.all()] 256 | else: 257 | self.fields['employee'].choices = [( 258 | x.id, x) for x in Employee.objects.all()] 259 | -------------------------------------------------------------------------------- /payslip/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.5 on 2016-04-29 10:51 3 | from __future__ import unicode_literals 4 | 5 | import datetime 6 | from django.conf import settings 7 | from django.db import migrations, models 8 | import django.db.models.deletion 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | initial = True 14 | 15 | dependencies = [ 16 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 17 | ] 18 | 19 | operations = [ 20 | migrations.CreateModel( 21 | name='Company', 22 | fields=[ 23 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 24 | ('name', models.CharField(max_length=100, verbose_name='Name')), 25 | ('address', models.TextField(blank=True, null=True, verbose_name='Address')), 26 | ], 27 | options={ 28 | 'ordering': ['name'], 29 | }, 30 | ), 31 | migrations.CreateModel( 32 | name='Employee', 33 | fields=[ 34 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 35 | ('hr_number', models.PositiveIntegerField(blank=True, null=True, verbose_name='HR number')), 36 | ('address', models.TextField(blank=True, null=True, verbose_name='Address')), 37 | ('title', models.CharField(choices=[(b'1', b'Ms.'), (b'2', b'Mrs.'), (b'3', b'Mr.'), (b'4', b'Dr.')], max_length=1, verbose_name='Title')), 38 | ('is_manager', models.BooleanField(default=False, verbose_name='is Manager')), 39 | ('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='employees', to='payslip.Company', verbose_name='Company')), 40 | ], 41 | options={ 42 | 'ordering': ['company__name', 'user__first_name'], 43 | }, 44 | ), 45 | migrations.CreateModel( 46 | name='ExtraField', 47 | fields=[ 48 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 49 | ('value', models.CharField(max_length=200, verbose_name='Value')), 50 | ], 51 | options={ 52 | 'ordering': ['field_type__name'], 53 | }, 54 | ), 55 | migrations.CreateModel( 56 | name='ExtraFieldType', 57 | fields=[ 58 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 59 | ('name', models.CharField(max_length=100, verbose_name='Name')), 60 | ('description', models.CharField(blank=True, max_length=100, null=True, verbose_name='Description')), 61 | ('model', models.CharField(blank=True, choices=[(b'Employee', b'Employee'), (b'Payment', b'Payment'), (b'Company', b'Company')], max_length=10, null=True, verbose_name='Model')), 62 | ('fixed_values', models.BooleanField(default=False, verbose_name='Fixed values')), 63 | ], 64 | options={ 65 | 'ordering': ['name'], 66 | }, 67 | ), 68 | migrations.CreateModel( 69 | name='Payment', 70 | fields=[ 71 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 72 | ('amount', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Amount')), 73 | ('date', models.DateTimeField(default=datetime.datetime(2016, 4, 29, 5, 51, 42, 30178), verbose_name='Date')), 74 | ('end_date', models.DateTimeField(blank=True, help_text='This field is only considered, if the payment type has a recurring rule.', null=True, verbose_name='End of recurring period')), 75 | ('description', models.CharField(blank=True, max_length=100, null=True, verbose_name='Description')), 76 | ('employee', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='payslip.Employee', verbose_name='Employee')), 77 | ('extra_fields', models.ManyToManyField(blank=True, null=True, to='payslip.ExtraField', verbose_name='Extra fields')), 78 | ], 79 | options={ 80 | 'ordering': ['employee__user__first_name', '-date'], 81 | }, 82 | ), 83 | migrations.CreateModel( 84 | name='PaymentType', 85 | fields=[ 86 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 87 | ('name', models.CharField(max_length=100, verbose_name='Name')), 88 | ('rrule', models.CharField(blank=True, choices=[(b'MONTHLY', 'Monthly'), (b'YEARLY', 'Yearly')], max_length=10, verbose_name='Recurring rule')), 89 | ('description', models.CharField(blank=True, max_length=100, null=True, verbose_name='Description')), 90 | ], 91 | options={ 92 | 'ordering': ['name'], 93 | }, 94 | ), 95 | migrations.AddField( 96 | model_name='payment', 97 | name='payment_type', 98 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='payslip.PaymentType', verbose_name='Payment type'), 99 | ), 100 | migrations.AddField( 101 | model_name='extrafield', 102 | name='field_type', 103 | field=models.ForeignKey(help_text='Only field types with fixed values can be chosen to add global values.', on_delete=django.db.models.deletion.CASCADE, related_name='extra_fields', to='payslip.ExtraFieldType', verbose_name='Field type'), 104 | ), 105 | migrations.AddField( 106 | model_name='employee', 107 | name='extra_fields', 108 | field=models.ManyToManyField(blank=True, null=True, to='payslip.ExtraField', verbose_name='Extra fields'), 109 | ), 110 | migrations.AddField( 111 | model_name='employee', 112 | name='user', 113 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='employees', to=settings.AUTH_USER_MODEL, verbose_name='User'), 114 | ), 115 | migrations.AddField( 116 | model_name='company', 117 | name='extra_fields', 118 | field=models.ManyToManyField(blank=True, null=True, to='payslip.ExtraField', verbose_name='Extra fields'), 119 | ), 120 | ] 121 | -------------------------------------------------------------------------------- /payslip/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlabstudio/django-payslip/55df83e9cba893f63b9380b23a74784d169972cb/payslip/migrations/__init__.py -------------------------------------------------------------------------------- /payslip/models.py: -------------------------------------------------------------------------------- 1 | """Models for the ``payslip`` application.""" 2 | from django.conf import settings 3 | from django.db import models 4 | from django.utils.timezone import localtime, now 5 | from django.utils.encoding import python_2_unicode_compatible 6 | from django.utils.translation import ugettext_lazy as _ 7 | 8 | 9 | @python_2_unicode_compatible 10 | class Company(models.Model): 11 | """ 12 | Model, which holds general information of a company. 13 | 14 | :name: Name of the company. 15 | :address: Full address model fields. 16 | :extra_fields: Custom fields to hold more information. 17 | 18 | """ 19 | name = models.CharField( 20 | max_length=100, 21 | verbose_name=_('Name'), 22 | ) 23 | 24 | address = models.TextField( 25 | verbose_name=_('Address'), 26 | blank=True, null=True, 27 | ) 28 | 29 | extra_fields = models.ManyToManyField( 30 | 'payslip.ExtraField', 31 | verbose_name=_('Extra fields'), 32 | blank=True, 33 | ) 34 | 35 | class Meta: 36 | ordering = ['name', ] 37 | 38 | def __str__(self): 39 | return '{0}'.format(self.name) 40 | 41 | 42 | @python_2_unicode_compatible 43 | class Employee(models.Model): 44 | """ 45 | Model, which holds personal information of employee. 46 | 47 | :user: Connection to the django user model, to allow a login. 48 | :company: Connection to the employee's company. 49 | :hr_number: ID to connect some non-digital documents or to use as a 50 | reference. 51 | :address: Full address model fields. 52 | :title: Title of the employee. 53 | :extra_fields: Custom fields like e.g. confession, tax class. 54 | 55 | """ 56 | user = models.ForeignKey( 57 | settings.AUTH_USER_MODEL, 58 | verbose_name=_('User'), 59 | related_name='employees', 60 | ) 61 | 62 | company = models.ForeignKey( 63 | 'payslip.Company', 64 | verbose_name=_('Company'), 65 | related_name='employees', 66 | ) 67 | 68 | hr_number = models.PositiveIntegerField( 69 | verbose_name=_('HR number'), 70 | blank=True, null=True, 71 | ) 72 | 73 | address = models.TextField( 74 | verbose_name=_('Address'), 75 | blank=True, null=True, 76 | ) 77 | 78 | title = models.CharField( 79 | max_length=1, 80 | verbose_name=_('Title'), 81 | choices=( 82 | ('1', 'Ms.'), 83 | ('2', 'Mrs.'), 84 | ('3', 'Mr.'), 85 | ('4', 'Dr.'), 86 | ) 87 | ) 88 | 89 | extra_fields = models.ManyToManyField( 90 | 'payslip.ExtraField', 91 | verbose_name=_('Extra fields'), 92 | blank=True, 93 | ) 94 | 95 | is_manager = models.BooleanField( 96 | default=False, 97 | verbose_name=_('is Manager'), 98 | ) 99 | 100 | class Meta: 101 | ordering = ['company__name', 'user__first_name', ] 102 | 103 | def __str__(self): 104 | return '{0} {1}'.format(self.user.first_name, self.user.last_name) 105 | 106 | 107 | @python_2_unicode_compatible 108 | class ExtraFieldType(models.Model): 109 | """ 110 | Model to create custom information holders. 111 | 112 | :name: Name of the attribute. 113 | :description: Description of the attribute. 114 | :model: Can be set in order to allow the use of only one model. 115 | :fixed_values: Can transform related exta fields into choices. 116 | 117 | """ 118 | name = models.CharField( 119 | max_length=100, 120 | verbose_name=_('Name'), 121 | ) 122 | 123 | description = models.CharField( 124 | max_length=100, 125 | blank=True, null=True, 126 | verbose_name=_('Description'), 127 | ) 128 | 129 | model = models.CharField( 130 | max_length=10, 131 | choices=( 132 | ('Employee', 'Employee'), 133 | ('Payment', 'Payment'), 134 | ('Company', 'Company'), 135 | ), 136 | verbose_name=_('Model'), 137 | blank=True, null=True, 138 | ) 139 | 140 | fixed_values = models.BooleanField( 141 | default=False, 142 | verbose_name=_('Fixed values'), 143 | ) 144 | 145 | class Meta: 146 | ordering = ['name', ] 147 | 148 | def __str__(self): 149 | return '{0}'.format(self.name) 150 | 151 | 152 | @python_2_unicode_compatible 153 | class ExtraField(models.Model): 154 | """ 155 | Model to create custom fields. 156 | 157 | :field_type: Connection to the field type. 158 | :value: Current value of this extra field. 159 | 160 | """ 161 | field_type = models.ForeignKey( 162 | 'payslip.ExtraFieldType', 163 | verbose_name=_('Field type'), 164 | related_name='extra_fields', 165 | help_text=_('Only field types with fixed values can be chosen to add' 166 | ' global values.'), 167 | ) 168 | 169 | value = models.CharField( 170 | max_length=200, 171 | verbose_name=_('Value'), 172 | ) 173 | 174 | class Meta: 175 | ordering = ['field_type__name', ] 176 | 177 | def __str__(self): 178 | return '{0} ({1}) - {2}'.format( 179 | self.field_type, self.field_type.get_model_display() or 'general', 180 | self.value) 181 | 182 | 183 | @python_2_unicode_compatible 184 | class PaymentType(models.Model): 185 | """ 186 | Model to create payment types. 187 | 188 | :name: Name of the type. 189 | :rrule: Recurring rule setting. 190 | :description: Description of the type. 191 | 192 | """ 193 | name = models.CharField( 194 | max_length=100, 195 | verbose_name=_('Name'), 196 | ) 197 | 198 | rrule = models.CharField( 199 | max_length=10, 200 | verbose_name=_('Recurring rule'), 201 | blank=True, 202 | choices=( 203 | ('MONTHLY', _('Monthly')), 204 | ('YEARLY', _('Yearly')), 205 | ) 206 | ) 207 | 208 | description = models.CharField( 209 | max_length=100, 210 | blank=True, null=True, 211 | verbose_name=_('Description'), 212 | ) 213 | 214 | class Meta: 215 | ordering = ['name', ] 216 | 217 | def __str__(self): 218 | if self.rrule: 219 | return '{0} ({1})'.format(self.name, self.get_rrule_display()) 220 | return '{0}'.format(self.name) 221 | 222 | 223 | @python_2_unicode_compatible 224 | class Payment(models.Model): 225 | """ 226 | Model, which represents one single payment. 227 | 228 | :payment_type: Type of the payment. 229 | :employee: Connection to the payment receiver. 230 | :amount: Current amount of the payment. 231 | :date: Date the payment should accrue. 232 | :end_date: Optional end date, if payment type has a rrule. 233 | :extra_fields: Custom fields like e.g. quantity, bonus. 234 | 235 | """ 236 | payment_type = models.ForeignKey( 237 | 'payslip.PaymentType', 238 | verbose_name=_('Payment type'), 239 | related_name='payments', 240 | ) 241 | 242 | employee = models.ForeignKey( 243 | 'payslip.Employee', 244 | verbose_name=_('Employee'), 245 | related_name='payments', 246 | ) 247 | 248 | amount = models.DecimalField( 249 | decimal_places=2, 250 | max_digits=10, 251 | verbose_name=_('Amount'), 252 | ) 253 | 254 | date = models.DateTimeField( 255 | verbose_name=_('Date'), 256 | default=now().today(), 257 | ) 258 | 259 | end_date = models.DateTimeField( 260 | verbose_name=_('End of recurring period'), 261 | blank=True, null=True, 262 | help_text=_('This field is only considered, if the payment type has a' 263 | ' recurring rule.'), 264 | ) 265 | 266 | extra_fields = models.ManyToManyField( 267 | 'payslip.ExtraField', 268 | verbose_name=_('Extra fields'), 269 | blank=True, 270 | ) 271 | 272 | description = models.CharField( 273 | max_length=100, 274 | blank=True, null=True, 275 | verbose_name=_('Description'), 276 | ) 277 | 278 | class Meta: 279 | ordering = ['employee__user__first_name', '-date', ] 280 | 281 | def __str__(self): 282 | return '{0} - {1} ({2})'.format(self.payment_type, self.amount, 283 | self.employee) 284 | 285 | def get_date_without_tz(self): 286 | return localtime(self.date).replace(tzinfo=None) 287 | 288 | def get_end_date_without_tz(self): 289 | return localtime(self.end_date).replace(tzinfo=None) 290 | 291 | @property 292 | def is_recurring(self): 293 | return self.payment_type.rrule 294 | -------------------------------------------------------------------------------- /payslip/south_migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # -*- coding: utf-8 -*- 3 | import datetime 4 | from south.db import db 5 | from south.v2 import SchemaMigration 6 | from django.db import models 7 | 8 | 9 | class Migration(SchemaMigration): 10 | 11 | def forwards(self, orm): 12 | # Adding model 'Company' 13 | db.create_table('payslip_company', ( 14 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 15 | ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), 16 | ('address', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), 17 | )) 18 | db.send_create_signal('payslip', ['Company']) 19 | 20 | # Adding M2M table for field extra_fields on 'Company' 21 | db.create_table('payslip_company_extra_fields', ( 22 | ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), 23 | ('company', models.ForeignKey(orm['payslip.company'], null=False)), 24 | ('extrafield', models.ForeignKey(orm['payslip.extrafield'], null=False)) 25 | )) 26 | db.create_unique('payslip_company_extra_fields', ['company_id', 'extrafield_id']) 27 | 28 | # Adding model 'Employee' 29 | db.create_table('payslip_employee', ( 30 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 31 | ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='employees', to=orm['auth.User'])), 32 | ('company', self.gf('django.db.models.fields.related.ForeignKey')(related_name='employees', to=orm['payslip.Company'])), 33 | ('hr_number', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True)), 34 | ('address', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), 35 | ('title', self.gf('django.db.models.fields.CharField')(max_length=1)), 36 | )) 37 | db.send_create_signal('payslip', ['Employee']) 38 | 39 | # Adding M2M table for field extra_fields on 'Employee' 40 | db.create_table('payslip_employee_extra_fields', ( 41 | ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), 42 | ('employee', models.ForeignKey(orm['payslip.employee'], null=False)), 43 | ('extrafield', models.ForeignKey(orm['payslip.extrafield'], null=False)) 44 | )) 45 | db.create_unique('payslip_employee_extra_fields', ['employee_id', 'extrafield_id']) 46 | 47 | # Adding model 'ExtraFieldType' 48 | db.create_table('payslip_extrafieldtype', ( 49 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 50 | ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), 51 | ('description', self.gf('django.db.models.fields.CharField')(max_length=100, null=True, blank=True)), 52 | )) 53 | db.send_create_signal('payslip', ['ExtraFieldType']) 54 | 55 | # Adding model 'ExtraField' 56 | db.create_table('payslip_extrafield', ( 57 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 58 | ('field_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='extra_fields', to=orm['payslip.ExtraFieldType'])), 59 | ('value', self.gf('django.db.models.fields.CharField')(max_length=200)), 60 | )) 61 | db.send_create_signal('payslip', ['ExtraField']) 62 | 63 | # Adding model 'PaymentType' 64 | db.create_table('payslip_paymenttype', ( 65 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 66 | ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), 67 | ('description', self.gf('django.db.models.fields.CharField')(max_length=100, null=True, blank=True)), 68 | )) 69 | db.send_create_signal('payslip', ['PaymentType']) 70 | 71 | # Adding model 'Payment' 72 | db.create_table('payslip_payment', ( 73 | ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), 74 | ('payment_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='payments', to=orm['payslip.PaymentType'])), 75 | ('employee', self.gf('django.db.models.fields.related.ForeignKey')(related_name='payments', to=orm['payslip.Employee'])), 76 | ('amount', self.gf('django.db.models.fields.DecimalField')(max_digits=10, decimal_places=2)), 77 | ('date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2013, 1, 5, 0, 0))), 78 | ('description', self.gf('django.db.models.fields.CharField')(max_length=100, null=True, blank=True)), 79 | )) 80 | db.send_create_signal('payslip', ['Payment']) 81 | 82 | # Adding M2M table for field extra_fields on 'Payment' 83 | db.create_table('payslip_payment_extra_fields', ( 84 | ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), 85 | ('payment', models.ForeignKey(orm['payslip.payment'], null=False)), 86 | ('extrafield', models.ForeignKey(orm['payslip.extrafield'], null=False)) 87 | )) 88 | db.create_unique('payslip_payment_extra_fields', ['payment_id', 'extrafield_id']) 89 | 90 | 91 | def backwards(self, orm): 92 | # Deleting model 'Company' 93 | db.delete_table('payslip_company') 94 | 95 | # Removing M2M table for field extra_fields on 'Company' 96 | db.delete_table('payslip_company_extra_fields') 97 | 98 | # Deleting model 'Employee' 99 | db.delete_table('payslip_employee') 100 | 101 | # Removing M2M table for field extra_fields on 'Employee' 102 | db.delete_table('payslip_employee_extra_fields') 103 | 104 | # Deleting model 'ExtraFieldType' 105 | db.delete_table('payslip_extrafieldtype') 106 | 107 | # Deleting model 'ExtraField' 108 | db.delete_table('payslip_extrafield') 109 | 110 | # Deleting model 'PaymentType' 111 | db.delete_table('payslip_paymenttype') 112 | 113 | # Deleting model 'Payment' 114 | db.delete_table('payslip_payment') 115 | 116 | # Removing M2M table for field extra_fields on 'Payment' 117 | db.delete_table('payslip_payment_extra_fields') 118 | 119 | 120 | models = { 121 | 'auth.group': { 122 | 'Meta': {'object_name': 'Group'}, 123 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 124 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), 125 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) 126 | }, 127 | 'auth.permission': { 128 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, 129 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 130 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), 131 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 132 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) 133 | }, 134 | 'auth.user': { 135 | 'Meta': {'object_name': 'User'}, 136 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 137 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), 138 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 139 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), 140 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 141 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 142 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 143 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 144 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 145 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 146 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), 147 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 148 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) 149 | }, 150 | 'contenttypes.contenttype': { 151 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 152 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 153 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 154 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 155 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 156 | }, 157 | 'payslip.company': { 158 | 'Meta': {'object_name': 'Company'}, 159 | 'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), 160 | 'extra_fields': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['payslip.ExtraField']", 'null': 'True', 'blank': 'True'}), 161 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 162 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 163 | }, 164 | 'payslip.employee': { 165 | 'Meta': {'object_name': 'Employee'}, 166 | 'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), 167 | 'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'employees'", 'to': "orm['payslip.Company']"}), 168 | 'extra_fields': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['payslip.ExtraField']", 'null': 'True', 'blank': 'True'}), 169 | 'hr_number': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), 170 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 171 | 'title': ('django.db.models.fields.CharField', [], {'max_length': '1'}), 172 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'employees'", 'to': "orm['auth.User']"}) 173 | }, 174 | 'payslip.extrafield': { 175 | 'Meta': {'object_name': 'ExtraField'}, 176 | 'field_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extra_fields'", 'to': "orm['payslip.ExtraFieldType']"}), 177 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 178 | 'value': ('django.db.models.fields.CharField', [], {'max_length': '200'}) 179 | }, 180 | 'payslip.extrafieldtype': { 181 | 'Meta': {'object_name': 'ExtraFieldType'}, 182 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), 183 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 184 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 185 | }, 186 | 'payslip.payment': { 187 | 'Meta': {'object_name': 'Payment'}, 188 | 'amount': ('django.db.models.fields.DecimalField', [], {'max_digits': '10', 'decimal_places': '2'}), 189 | 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2013, 1, 5, 0, 0)'}), 190 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), 191 | 'employee': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'payments'", 'to': "orm['payslip.Employee']"}), 192 | 'extra_fields': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['payslip.ExtraField']", 'null': 'True', 'blank': 'True'}), 193 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 194 | 'payment_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'payments'", 'to': "orm['payslip.PaymentType']"}) 195 | }, 196 | 'payslip.paymenttype': { 197 | 'Meta': {'object_name': 'PaymentType'}, 198 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), 199 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 200 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 201 | } 202 | } 203 | 204 | complete_apps = ['payslip'] -------------------------------------------------------------------------------- /payslip/south_migrations/0002_auto__add_field_employee_is_manager.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # -*- coding: utf-8 -*- 3 | import datetime 4 | from south.db import db 5 | from south.v2 import SchemaMigration 6 | from django.db import models 7 | 8 | 9 | class Migration(SchemaMigration): 10 | 11 | def forwards(self, orm): 12 | # Adding field 'Employee.is_manager' 13 | db.add_column('payslip_employee', 'is_manager', 14 | self.gf('django.db.models.fields.BooleanField')(default=False), 15 | keep_default=False) 16 | 17 | 18 | def backwards(self, orm): 19 | # Deleting field 'Employee.is_manager' 20 | db.delete_column('payslip_employee', 'is_manager') 21 | 22 | 23 | models = { 24 | 'auth.group': { 25 | 'Meta': {'object_name': 'Group'}, 26 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 27 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), 28 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) 29 | }, 30 | 'auth.permission': { 31 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, 32 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 33 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), 34 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 35 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) 36 | }, 37 | 'auth.user': { 38 | 'Meta': {'object_name': 'User'}, 39 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 40 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), 41 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 42 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), 43 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 44 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 45 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 46 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 47 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 48 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 49 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), 50 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 51 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) 52 | }, 53 | 'contenttypes.contenttype': { 54 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 55 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 56 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 57 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 58 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 59 | }, 60 | 'payslip.company': { 61 | 'Meta': {'object_name': 'Company'}, 62 | 'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), 63 | 'extra_fields': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['payslip.ExtraField']", 'null': 'True', 'blank': 'True'}), 64 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 65 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 66 | }, 67 | 'payslip.employee': { 68 | 'Meta': {'object_name': 'Employee'}, 69 | 'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), 70 | 'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'employees'", 'to': "orm['payslip.Company']"}), 71 | 'extra_fields': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['payslip.ExtraField']", 'null': 'True', 'blank': 'True'}), 72 | 'hr_number': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), 73 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 74 | 'is_manager': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 75 | 'title': ('django.db.models.fields.CharField', [], {'max_length': '1'}), 76 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'employees'", 'to': "orm['auth.User']"}) 77 | }, 78 | 'payslip.extrafield': { 79 | 'Meta': {'object_name': 'ExtraField'}, 80 | 'field_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extra_fields'", 'to': "orm['payslip.ExtraFieldType']"}), 81 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 82 | 'value': ('django.db.models.fields.CharField', [], {'max_length': '200'}) 83 | }, 84 | 'payslip.extrafieldtype': { 85 | 'Meta': {'object_name': 'ExtraFieldType'}, 86 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), 87 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 88 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 89 | }, 90 | 'payslip.payment': { 91 | 'Meta': {'object_name': 'Payment'}, 92 | 'amount': ('django.db.models.fields.DecimalField', [], {'max_digits': '10', 'decimal_places': '2'}), 93 | 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2013, 1, 6, 0, 0)'}), 94 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), 95 | 'employee': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'payments'", 'to': "orm['payslip.Employee']"}), 96 | 'extra_fields': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['payslip.ExtraField']", 'null': 'True', 'blank': 'True'}), 97 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 98 | 'payment_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'payments'", 'to': "orm['payslip.PaymentType']"}) 99 | }, 100 | 'payslip.paymenttype': { 101 | 'Meta': {'object_name': 'PaymentType'}, 102 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), 103 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 104 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 105 | } 106 | } 107 | 108 | complete_apps = ['payslip'] -------------------------------------------------------------------------------- /payslip/south_migrations/0003_auto__add_field_extrafieldtype_model__add_field_extrafieldtype_fixed_v.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # -*- coding: utf-8 -*- 3 | import datetime 4 | from south.db import db 5 | from south.v2 import SchemaMigration 6 | from django.db import models 7 | 8 | 9 | class Migration(SchemaMigration): 10 | 11 | def forwards(self, orm): 12 | # Adding field 'ExtraFieldType.model' 13 | db.add_column('payslip_extrafieldtype', 'model', 14 | self.gf('django.db.models.fields.CharField')(max_length=1, null=True, blank=True), 15 | keep_default=False) 16 | 17 | # Adding field 'ExtraFieldType.fixed_values' 18 | db.add_column('payslip_extrafieldtype', 'fixed_values', 19 | self.gf('django.db.models.fields.BooleanField')(default=False), 20 | keep_default=False) 21 | 22 | 23 | def backwards(self, orm): 24 | # Deleting field 'ExtraFieldType.model' 25 | db.delete_column('payslip_extrafieldtype', 'model') 26 | 27 | # Deleting field 'ExtraFieldType.fixed_values' 28 | db.delete_column('payslip_extrafieldtype', 'fixed_values') 29 | 30 | 31 | models = { 32 | 'auth.group': { 33 | 'Meta': {'object_name': 'Group'}, 34 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 35 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), 36 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) 37 | }, 38 | 'auth.permission': { 39 | 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, 40 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 41 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), 42 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 43 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) 44 | }, 45 | 'auth.user': { 46 | 'Meta': {'object_name': 'User'}, 47 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 48 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), 49 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 50 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), 51 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 52 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 53 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 54 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 55 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 56 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 57 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), 58 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 59 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) 60 | }, 61 | 'contenttypes.contenttype': { 62 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 63 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 64 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 65 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 66 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 67 | }, 68 | 'payslip.company': { 69 | 'Meta': {'object_name': 'Company'}, 70 | 'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), 71 | 'extra_fields': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['payslip.ExtraField']", 'null': 'True', 'blank': 'True'}), 72 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 73 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 74 | }, 75 | 'payslip.employee': { 76 | 'Meta': {'object_name': 'Employee'}, 77 | 'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), 78 | 'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'employees'", 'to': "orm['payslip.Company']"}), 79 | 'extra_fields': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['payslip.ExtraField']", 'null': 'True', 'blank': 'True'}), 80 | 'hr_number': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), 81 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 82 | 'is_manager': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 83 | 'title': ('django.db.models.fields.CharField', [], {'max_length': '1'}), 84 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'employees'", 'to': "orm['auth.User']"}) 85 | }, 86 | 'payslip.extrafield': { 87 | 'Meta': {'object_name': 'ExtraField'}, 88 | 'field_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extra_fields'", 'to': "orm['payslip.ExtraFieldType']"}), 89 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 90 | 'value': ('django.db.models.fields.CharField', [], {'max_length': '200'}) 91 | }, 92 | 'payslip.extrafieldtype': { 93 | 'Meta': {'object_name': 'ExtraFieldType'}, 94 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), 95 | 'fixed_values': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 96 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 97 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '1', 'null': 'True', 'blank': 'True'}), 98 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 99 | }, 100 | 'payslip.payment': { 101 | 'Meta': {'object_name': 'Payment'}, 102 | 'amount': ('django.db.models.fields.DecimalField', [], {'max_digits': '10', 'decimal_places': '2'}), 103 | 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2013, 1, 8, 0, 0)'}), 104 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), 105 | 'employee': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'payments'", 'to': "orm['payslip.Employee']"}), 106 | 'extra_fields': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['payslip.ExtraField']", 'null': 'True', 'blank': 'True'}), 107 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 108 | 'payment_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'payments'", 'to': "orm['payslip.PaymentType']"}) 109 | }, 110 | 'payslip.paymenttype': { 111 | 'Meta': {'object_name': 'PaymentType'}, 112 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), 113 | 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 114 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 115 | } 116 | } 117 | 118 | complete_apps = ['payslip'] -------------------------------------------------------------------------------- /payslip/south_migrations/0004_auto__add_field_paymenttype_rrule__chg_field_extrafieldtype_model.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # -*- coding: utf-8 -*- 3 | import datetime 4 | from south.db import db 5 | from south.v2 import SchemaMigration 6 | from django.db import models 7 | 8 | 9 | class Migration(SchemaMigration): 10 | 11 | def forwards(self, orm): 12 | # Adding field 'PaymentType.rrule' 13 | db.add_column(u'payslip_paymenttype', 'rrule', 14 | self.gf('django.db.models.fields.CharField')(default='', max_length=10, blank=True), 15 | keep_default=False) 16 | 17 | 18 | # Changing field 'ExtraFieldType.model' 19 | db.alter_column(u'payslip_extrafieldtype', 'model', self.gf('django.db.models.fields.CharField')(max_length=10, null=True)) 20 | 21 | def backwards(self, orm): 22 | # Deleting field 'PaymentType.rrule' 23 | db.delete_column(u'payslip_paymenttype', 'rrule') 24 | 25 | 26 | # Changing field 'ExtraFieldType.model' 27 | db.alter_column(u'payslip_extrafieldtype', 'model', self.gf('django.db.models.fields.CharField')(max_length=1, null=True)) 28 | 29 | models = { 30 | u'auth.group': { 31 | 'Meta': {'object_name': 'Group'}, 32 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 33 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), 34 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) 35 | }, 36 | u'auth.permission': { 37 | 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, 38 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 39 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), 40 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 41 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) 42 | }, 43 | u'auth.user': { 44 | 'Meta': {'object_name': 'User'}, 45 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 46 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), 47 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 48 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), 49 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 50 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 51 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 52 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 53 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 54 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 55 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), 56 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 57 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) 58 | }, 59 | u'contenttypes.contenttype': { 60 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 61 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 62 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 63 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 64 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 65 | }, 66 | u'payslip.company': { 67 | 'Meta': {'object_name': 'Company'}, 68 | 'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), 69 | 'extra_fields': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['payslip.ExtraField']", 'null': 'True', 'blank': 'True'}), 70 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 71 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 72 | }, 73 | u'payslip.employee': { 74 | 'Meta': {'object_name': 'Employee'}, 75 | 'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), 76 | 'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'employees'", 'to': u"orm['payslip.Company']"}), 77 | 'extra_fields': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['payslip.ExtraField']", 'null': 'True', 'blank': 'True'}), 78 | 'hr_number': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), 79 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 80 | 'is_manager': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 81 | 'title': ('django.db.models.fields.CharField', [], {'max_length': '1'}), 82 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'employees'", 'to': u"orm['auth.User']"}) 83 | }, 84 | u'payslip.extrafield': { 85 | 'Meta': {'object_name': 'ExtraField'}, 86 | 'field_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extra_fields'", 'to': u"orm['payslip.ExtraFieldType']"}), 87 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 88 | 'value': ('django.db.models.fields.CharField', [], {'max_length': '200'}) 89 | }, 90 | u'payslip.extrafieldtype': { 91 | 'Meta': {'object_name': 'ExtraFieldType'}, 92 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), 93 | 'fixed_values': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 94 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 95 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), 96 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 97 | }, 98 | u'payslip.payment': { 99 | 'Meta': {'object_name': 'Payment'}, 100 | 'amount': ('django.db.models.fields.DecimalField', [], {'max_digits': '10', 'decimal_places': '2'}), 101 | 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2013, 5, 7, 0, 0)'}), 102 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), 103 | 'employee': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'payments'", 'to': u"orm['payslip.Employee']"}), 104 | 'extra_fields': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['payslip.ExtraField']", 'null': 'True', 'blank': 'True'}), 105 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 106 | 'payment_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'payments'", 'to': u"orm['payslip.PaymentType']"}) 107 | }, 108 | u'payslip.paymenttype': { 109 | 'Meta': {'object_name': 'PaymentType'}, 110 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), 111 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 112 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 113 | 'rrule': ('django.db.models.fields.CharField', [], {'max_length': '10', 'blank': 'True'}) 114 | } 115 | } 116 | 117 | complete_apps = ['payslip'] -------------------------------------------------------------------------------- /payslip/south_migrations/0005_auto__add_field_payment_end_date.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # -*- coding: utf-8 -*- 3 | import datetime 4 | from south.db import db 5 | from south.v2 import SchemaMigration 6 | from django.db import models 7 | 8 | 9 | class Migration(SchemaMigration): 10 | 11 | def forwards(self, orm): 12 | # Adding field 'Payment.end_date' 13 | db.add_column(u'payslip_payment', 'end_date', 14 | self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True), 15 | keep_default=False) 16 | 17 | 18 | def backwards(self, orm): 19 | # Deleting field 'Payment.end_date' 20 | db.delete_column(u'payslip_payment', 'end_date') 21 | 22 | 23 | models = { 24 | u'auth.group': { 25 | 'Meta': {'object_name': 'Group'}, 26 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 27 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), 28 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) 29 | }, 30 | u'auth.permission': { 31 | 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, 32 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 33 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), 34 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 35 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) 36 | }, 37 | u'auth.user': { 38 | 'Meta': {'object_name': 'User'}, 39 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 40 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), 41 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 42 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), 43 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 44 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 45 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 46 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 47 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 48 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 49 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), 50 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), 51 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) 52 | }, 53 | u'contenttypes.contenttype': { 54 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 55 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 56 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 57 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 58 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 59 | }, 60 | u'payslip.company': { 61 | 'Meta': {'object_name': 'Company'}, 62 | 'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), 63 | 'extra_fields': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['payslip.ExtraField']", 'null': 'True', 'blank': 'True'}), 64 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 65 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 66 | }, 67 | u'payslip.employee': { 68 | 'Meta': {'object_name': 'Employee'}, 69 | 'address': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), 70 | 'company': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'employees'", 'to': u"orm['payslip.Company']"}), 71 | 'extra_fields': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['payslip.ExtraField']", 'null': 'True', 'blank': 'True'}), 72 | 'hr_number': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), 73 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 74 | 'is_manager': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 75 | 'title': ('django.db.models.fields.CharField', [], {'max_length': '1'}), 76 | 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'employees'", 'to': u"orm['auth.User']"}) 77 | }, 78 | u'payslip.extrafield': { 79 | 'Meta': {'object_name': 'ExtraField'}, 80 | 'field_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'extra_fields'", 'to': u"orm['payslip.ExtraFieldType']"}), 81 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 82 | 'value': ('django.db.models.fields.CharField', [], {'max_length': '200'}) 83 | }, 84 | u'payslip.extrafieldtype': { 85 | 'Meta': {'object_name': 'ExtraFieldType'}, 86 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), 87 | 'fixed_values': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 88 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 89 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), 90 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) 91 | }, 92 | u'payslip.payment': { 93 | 'Meta': {'object_name': 'Payment'}, 94 | 'amount': ('django.db.models.fields.DecimalField', [], {'max_digits': '10', 'decimal_places': '2'}), 95 | 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2013, 5, 7, 0, 0)'}), 96 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), 97 | 'employee': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'payments'", 'to': u"orm['payslip.Employee']"}), 98 | 'end_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), 99 | 'extra_fields': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['payslip.ExtraField']", 'null': 'True', 'blank': 'True'}), 100 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 101 | 'payment_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'payments'", 'to': u"orm['payslip.PaymentType']"}) 102 | }, 103 | u'payslip.paymenttype': { 104 | 'Meta': {'object_name': 'PaymentType'}, 105 | 'description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), 106 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 107 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 108 | 'rrule': ('django.db.models.fields.CharField', [], {'max_length': '10', 'blank': 'True'}) 109 | } 110 | } 111 | 112 | complete_apps = ['payslip'] -------------------------------------------------------------------------------- /payslip/south_migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitlabstudio/django-payslip/55df83e9cba893f63b9380b23a74784d169972cb/payslip/south_migrations/__init__.py -------------------------------------------------------------------------------- /payslip/static/payslip/css/base.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 60px; 3 | } -------------------------------------------------------------------------------- /payslip/static/payslip/css/payslip.css: -------------------------------------------------------------------------------- 1 | @media print { 2 | @page { 3 | 4 | -pdf-page-orientation: portrait; 5 | -pdf-page-size: a4; 6 | 7 | margin-top: 2cm; 8 | margin-left: 3cm; 9 | margin-right: 2cm; 10 | margin-bottom: 3cm; 11 | 12 | } 13 | 14 | @page regular { 15 | 16 | -pdf-page-orientation: portrait; 17 | -pdf-page-size: a4; 18 | 19 | margin-top: 2cm; 20 | margin-left: 3cm; 21 | margin-right: 2cm; 22 | margin-bottom: 3cm; 23 | 24 | @frame footer { 25 | -pdf-frame-content: footer; 26 | -pdf-frame-border: 1pt solid black; 27 | bottom: 2cm; 28 | left: 3cm; 29 | right: 2cm; 30 | height: 1cm; 31 | } 32 | } 33 | 34 | html { 35 | border: none; 36 | display: inline; 37 | width: auto; 38 | height: auto; 39 | white-space: normal; 40 | } 41 | 42 | html, p, div, span, h1, h2, table, tr, th, td { 43 | margin: 0; 44 | padding: 0; 45 | } 46 | 47 | address, blockquote, body, center, dl, dir, div, fieldset, form, h1, h2, h3, h4, h5, h6, hr, isindex, menu, noframes, noscript, ol, p, pre, ul, li, dd, dt, pdftoc { 48 | display: block; 49 | } 50 | 51 | #payslipMenu { 52 | display: none; 53 | } 54 | 55 | .tdMiddle { 56 | max-width: 55%; 57 | } 58 | 59 | .tdMini { 60 | max-width: 20%; 61 | } 62 | 63 | .tdSmall { 64 | max-width: 30%; 65 | } 66 | } 67 | 68 | @media screen { 69 | body { 70 | width: 210mm; 71 | margin-top: 55px; 72 | margin-bottom: 100px; 73 | margin-left: auto; 74 | margin-right: auto; 75 | } 76 | 77 | h1 { 78 | width: 115mm; 79 | } 80 | 81 | p { 82 | margin: 0; 83 | } 84 | 85 | #payslipMenu { 86 | position: fixed; 87 | background: #000000; 88 | padding: 5px 0; 89 | width: 100%; 90 | top: 0px; 91 | left: 0px; 92 | height: 27px; 93 | border-bottom: 1px solid #ccc; 94 | box-shadow: 0px 0px 30px #bbb; 95 | } 96 | 97 | #payslipMenu a { 98 | color: #ffffff; 99 | } 100 | 101 | #payslipMenu form { 102 | display: inline; 103 | } 104 | 105 | #payslipMenu form>p { 106 | display: none; 107 | } 108 | 109 | .printButton { 110 | margin-left: 20px; 111 | } 112 | } 113 | 114 | html { 115 | font-weight: normal; 116 | color: #000000; 117 | background-color: transparent; 118 | line-height: 110%; 119 | font-family: Verdana, Tahoma, "Trebuchet MS", "DejuVu Sans", "Bitstream Vera Sans", sans-serif; 120 | font-size: 12px; 121 | } 122 | 123 | #address #addressEmployee, .box .boxContent, #payoutSum, #sum, .altFont { 124 | font-family: "Courier New", Courier, "Lucida Sans Typewriter", "Lucida Typewriter", monospace; 125 | } 126 | 127 | #address { 128 | width: 57%; 129 | } 130 | 131 | #addressCompany { 132 | font-size: 9px; 133 | padding: 5px 10px; 134 | border: 1px solid #000000; 135 | margin-right: 20px; 136 | } 137 | 138 | #addressEmployee { 139 | padding: 12px; 140 | border-left: 1px solid #000000; 141 | border-right: 1px solid #000000; 142 | border-bottom: 1px solid #000000; 143 | font-weight: bold; 144 | margin-right: 20px; 145 | } 146 | 147 | .box { 148 | border: 1px solid #000000; 149 | padding: 5px 10px 0px; 150 | margin-right: 5px; 151 | } 152 | 153 | .box .boxContent { 154 | white-space: nowrap; 155 | font-size: 80%; 156 | } 157 | 158 | .box .boxHead { 159 | font-size: 70%; 160 | white-space: nowrap; 161 | } 162 | 163 | #employeeExtraFields { 164 | width: 55%; 165 | } 166 | 167 | h1, h2, h3, h4, h5, h6 { 168 | font-weight:bold; 169 | -pdf-outline: true; 170 | -pdf-outline-open: false; 171 | } 172 | 173 | h1 { 174 | font-size:160%; 175 | -pdf-outline-level: 0; 176 | } 177 | 178 | h2 { 179 | font-size: 110%; 180 | margin-top: 50px; 181 | margin-bottom: 10px; 182 | -pdf-outline-level: 1; 183 | padding: 5px; 184 | line-height: 140%; 185 | background-color: #222222; 186 | color: #ffffff; 187 | } 188 | 189 | h3 { 190 | font-size:100%; 191 | -pdf-outline-level: 2; 192 | } 193 | 194 | h4 { 195 | -pdf-outline-level: 3; 196 | } 197 | 198 | h5 { 199 | -pdf-outline-level: 4; 200 | } 201 | 202 | h6 { 203 | -pdf-outline-level: 5; 204 | } 205 | 206 | #payout { 207 | float: left; 208 | } 209 | 210 | #payoutHead { 211 | text-align: right; 212 | padding: 13px 10px 8px; 213 | } 214 | 215 | #payoutCurrency, #payoutSum, #payoutHead { 216 | font-weight: bold; 217 | } 218 | 219 | #payoutCurrency, #payoutSum { 220 | border-top: 3px solid #000000; 221 | border-bottom: 3px solid #000000; 222 | border-left: 1px solid #000000; 223 | border-right: 1px solid #000000; 224 | padding: 10px 10px 5px; 225 | } 226 | 227 | .subHead { 228 | text-align: right; 229 | font-weight: bold; 230 | margin-top: 10px; 231 | margin-bottom: 70px; 232 | } 233 | 234 | .sum { 235 | font-size: 10px; 236 | margin: 5px 9px 5px 0px; 237 | float: left; 238 | } 239 | 240 | .sum strong { 241 | font-size: 14px; 242 | } 243 | 244 | table, thead, tbody, tr { 245 | width: 100%; 246 | } 247 | 248 | table tr { 249 | padding-bottom: 5px; 250 | } 251 | 252 | td, th { 253 | vertical-align: top; 254 | } 255 | 256 | th { 257 | font-size: 90%; 258 | text-align: left; 259 | font-weight: bold; 260 | } -------------------------------------------------------------------------------- /payslip/static/payslip/js/payslip.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('#payslipMenu .printButton').click(function() { 3 | $('#payslipMenu').remove(); 4 | window.print(); 5 | return false; 6 | }); 7 | }); -------------------------------------------------------------------------------- /payslip/templates/payslip/company_confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends "payslip/delete_base.html" %} 2 | {% load i18n %} 3 | 4 | {% block text %}
{% trans "Are you sure that you want to delete this company?" %}
{% endblock %} -------------------------------------------------------------------------------- /payslip/templates/payslip/company_form.html: -------------------------------------------------------------------------------- 1 | {% extends "payslip/payslip_base.html" %} 2 | {% load i18n %} 3 | 4 | {% block head %} 5 | {% if not form.instance.id %} 6 |{% trans "Host multiple companies or divisions." %}
15 |{% trans "Name" %} | 18 |{% trans "Action" %} | 19 |
---|---|
{{ company }} | 23 |24 | {% trans "Update" %} 25 | {% trans "Delete" %} 26 | | 27 |
{% trans "No companies defined." %} | 31 |
{% trans "Add personal data of the employees." %}
39 |{% trans "Name" %} | 42 |{% trans "Company" %} | 43 |{% trans "Action" %} | 44 |
---|---|---|
{{ employee }} | 48 |{{ employee.company }} | 49 |50 | {% trans "Update" %} 51 | {% trans "Delete" %} 52 | | 53 |
{% trans "No employees defined." %} | 57 |
{% trans "Define single or recurring payment types." %}
68 |{% trans "Name" %} | 71 |{% trans "Action" %} | 72 |
---|---|
{{ payment_type }} | 76 |77 | {% trans "Update" %} 78 | {% trans "Delete" %} 79 | | 80 |
{% trans "No payment types defined." %} | 84 |
{% trans "Add payments to an employee." %}
92 |{% trans "Type" %} | 95 |{% trans "Employee" %} | 96 |{% trans "Amount" %} | 97 |{% trans "Date" %} | 98 |{% trans "End" %} | 99 |{% trans "Action" %} | 100 |
---|---|---|---|---|---|
{{ payment.payment_type }} | 104 |{{ payment.employee }} | 105 |{{ payment.amount }} | 106 |{{ payment.date|date }} | 107 |{{ payment.end_date|date }} | 108 |109 | {% trans "Update" %} 110 | {% trans "Delete" %} 111 | | 112 |
{% trans "No payments defined." %} | 116 |
{% trans "Define extra field types for companies, employees and payments." %}
127 |{% trans "Name" %} | 130 |{% trans "Action" %} | 131 |
---|---|
{{ type }} | 135 |136 | {% trans "Update" %} 137 | {% trans "Delete" %} 138 | | 139 |
{% trans "No extra field types defined." %} | 143 |
{% trans "Define fixed values for extra field types." %}
151 |{% trans "Name" %} | 154 |{% trans "Action" %} | 155 |
---|---|
{{ field }} | 159 |160 | {% trans "Update" %} 161 | {% trans "Delete" %} 162 | | 163 |
{% trans "No extra fields defined." %} | 167 |
{% trans "Are you sure that you want to delete this employee?" %}
{% endblock %} -------------------------------------------------------------------------------- /payslip/templates/payslip/employee_form.html: -------------------------------------------------------------------------------- 1 | {% extends "payslip/payslip_base.html" %} 2 | {% load i18n %} 3 | 4 | {% block head %} 5 | {% if not form.instance.id %} 6 |{% trans "Are you sure that you want to delete this extra field?" %}
{% endblock %} -------------------------------------------------------------------------------- /payslip/templates/payslip/extrafield_form.html: -------------------------------------------------------------------------------- 1 | {% extends "payslip/payslip_base.html" %} 2 | {% load i18n %} 3 | 4 | {% block head %} 5 | {% if not form.instance.id %} 6 |{% trans "Are you sure that you want to delete this extra field type?" %}
{% endblock %} -------------------------------------------------------------------------------- /payslip/templates/payslip/extrafieldtype_form.html: -------------------------------------------------------------------------------- 1 | {% extends "payslip/payslip_base.html" %} 2 | {% load i18n %} 3 | 4 | {% block head %} 5 | {% if not form.instance.id %} 6 |{% trans "Are you sure that you want to delete this payment?" %}
{% endblock %} -------------------------------------------------------------------------------- /payslip/templates/payslip/payment_form.html: -------------------------------------------------------------------------------- 1 | {% extends "payslip/payslip_base.html" %} 2 | {% load i18n %} 3 | 4 | {% block head %} 5 | {% if not form.instance.id %} 6 |{% trans "Are you sure that you want to delete this payment type?" %}
{% endblock %} -------------------------------------------------------------------------------- /payslip/templates/payslip/paymenttype_form.html: -------------------------------------------------------------------------------- 1 | {% extends "payslip/payslip_base.html" %} 2 | {% load i18n %} 3 | 4 | {% block head %} 5 | {% if not form.instance.id %} 6 |{% trans "Payslip" %} |
37 |
38 |
39 | {% trans "Printed date" %}: |
43 |
44 |
45 | {% trans "Period" %}: |
49 |
50 |
51 | {% trans "HR nr." %}: |
55 |
{% trans "Considered as income receipt. Please store it carefully." %}
59 |
63 | {{ employee.company }}, {{ employee.company.address }} 64 |
65 | {{ employee.get_title_display }} |
70 |
71 |
|
91 |
{% trans "Payment type" %} | 100 | {% for field_type in payment_extra_fields %} 101 |{{ field_type.name }} | 102 | {% endfor %} 103 |{% trans "Month" %} | 104 |{% trans "Amount" %} | 105 |
---|---|---|---|
{{ payment.payment_type.name }} | 111 | {% for field_type in payment_extra_fields %} 112 |{{ field_type|get_extra_field_value:payment }} | 113 | {% endfor %} 114 |{% if payment.is_recurring %}{{ date_end|date:"M Y" }}{% else %}{{ payment.date|date:"M Y" }}{% endif %} | 115 |{{ payment.amount|floatformat:2 }} | 116 |
{% trans "Sum earnings" %}: {{ sum|floatformat:2 }}
122 |{% trans "Sum deductions" %}: {{ sum_neg|floatformat:2 }}
123 |
128 |
129 | {% trans "Gross earnings" %}: |
133 |
134 | {% trans "Payout" %}: 135 | |
136 |
137 | {{ currency }} 138 | |
139 |
140 | {{ sum|add:sum_neg|floatformat:2 }} 141 | |
142 |
150 |
151 | {% trans "Gross total" %}: |
155 |
156 |
157 | {% trans "Net total" %}: |
161 |