├── .idea ├── .name ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── vfOA.iml └── workspace.xml ├── README.md ├── db111.sqlite3 ├── demo ├── README.rst ├── __init__.py ├── bloodtest │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── flows.py │ ├── forms.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── bloodtest │ │ │ └── bloodtest │ │ │ ├── biochemical_data.html │ │ │ ├── first_sample.html │ │ │ ├── hormone_data.html │ │ │ └── second_sample.html │ └── views.py ├── countersign │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── flows.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20171127_1554.py │ │ └── __init__.py │ ├── models.py │ └── tests.py ├── customnode │ ├── README.rst │ ├── __init__.py │ ├── doc │ │ └── DynamicSplit.png │ ├── flows.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── nodes.py │ ├── urls.py │ └── views.py ├── employees │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── locale │ │ ├── de │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ ├── es │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ ├── fr │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ ├── ko │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ ├── ru │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ └── zh_Hans │ │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── managers.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_i18n.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── employees │ │ │ ├── base_module.html │ │ │ ├── change_manager.html │ │ │ ├── change_salary.html │ │ │ ├── change_title.html │ │ │ ├── department_detail.html │ │ │ ├── department_employees.html │ │ │ ├── employee_detail.html │ │ │ └── menu.html │ ├── urls.py │ └── views.py ├── hr │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── integration │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── locale │ │ ├── de │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ ├── es │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ ├── fr │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ ├── ko │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ ├── ru │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ └── zh_Hans │ │ │ └── LC_MESSAGES │ │ │ ├── django.mo │ │ │ └── django.po │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_i18n.py │ │ ├── 0003_auto_20171128_1814.py │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── integration │ │ │ ├── base_module.html │ │ │ └── menu.html │ ├── urls.py │ └── views.py ├── leave │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── flows.py │ ├── forms.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_leave_comment.py │ │ ├── 0003_auto_20171130_0922.py │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── settings.py ├── static │ ├── demo │ │ ├── css │ │ │ ├── shepherd-theme-arrows.css │ │ │ └── vfOA.css │ │ └── js │ │ │ ├── jquery.cookie.js │ │ │ └── shepherd.js │ └── material │ │ ├── css │ │ ├── materialize.admin.css │ │ ├── materialize.admin.min.css │ │ ├── materialize.css │ │ ├── materialize.forms.css │ │ ├── materialize.frontend.css │ │ └── materialize.frontend.min.css │ │ └── imgs │ │ ├── background.svg │ │ └── sidenav.svg ├── templates │ ├── admin │ │ └── testing │ │ │ └── Contract │ │ │ └── change_list.html │ ├── demo │ │ ├── base_module.html │ │ ├── index.html │ │ └── wizard.html │ ├── hr │ │ └── menu.html │ ├── leave │ │ ├── check.html │ │ ├── menu.html │ │ └── start.html │ └── material │ │ ├── fields │ │ ├── _django_rangeinput.html │ │ ├── django_checkboxinput.html │ │ ├── django_checkboxselectmultiple.html │ │ ├── django_clearablefileinput.html │ │ ├── django_dateinput.html │ │ ├── django_datetimeinput.html │ │ ├── django_fileinput.html │ │ ├── django_input.html │ │ ├── django_nullbooleanselect.html │ │ ├── django_radioselect.html │ │ ├── django_select.html │ │ ├── django_selectdatewidget.html │ │ ├── django_selectmultiple.html │ │ ├── django_splitdatetimewidget.html │ │ ├── django_textarea.html │ │ └── django_timeinput.html │ │ └── frontend │ │ └── base_site.html ├── testing │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── migrations │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py ├── urls.py └── website.py ├── img ├── 1.PNG ├── 2.PNG ├── 3.PNG ├── 4.PNG └── 5.PNG ├── manage.py └── requirements.txt /.idea/.name: -------------------------------------------------------------------------------- 1 | vfOA -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | AngularJS 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vfOA.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | vfOA 2 | ========== 3 | 4 | vfOA是基于Viewflow的OA演示系统,能够快速实现数据的CURD以及流程处理,可开发轻量级OA/CRM/ERP等系统。 5 | 6 | 更多内容请参考django-viewflow、django-materia 7 | ## Installation and Dependencies: 8 | 9 | django==1.11.7 10 | 11 | django-filter==1.1.0 12 | 13 | django-formtools==2.1 14 | 15 | django-material==1.1.1 16 | 17 | django-viewflow==1.1.0 18 | 19 | ## Quick start: 20 | pip install -r requirements.txt 21 | 22 | python manage.py runserver 8888 23 | 24 | ## Photos: 25 | 编辑界面 26 | ![编辑界面](https://raw.githubusercontent.com/htwenhe/vfOA/master/img/1.PNG) 27 | 28 | 列表页面 29 | ![列表页面](https://raw.githubusercontent.com/htwenhe/vfOA/master/img/2.PNG) 30 | 31 | 代办事项 32 | ![代办事项](https://raw.githubusercontent.com/htwenhe/vfOA/master/img/3.PNG) 33 | 34 | 流程处理 35 | ![流程处理](https://raw.githubusercontent.com/htwenhe/vfOA/master/img/4.PNG) 36 | 37 | 流程信息展示 38 | ![流程信息展示](https://raw.githubusercontent.com/htwenhe/vfOA/master/img/5.PNG) 39 | -------------------------------------------------------------------------------- /db111.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/db111.sqlite3 -------------------------------------------------------------------------------- /demo/README.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | Viewflow Demo 3 | ============= 4 | 5 | See live at: http://demo.viewflow.io or checkout and run: 6 | 7 | .. code-block:: shell 8 | 9 | tox migrate 10 | tox loaddata tests/helloworld/fixtures/default_data.json 11 | tox loaddata tests/shipment/fixtures/default_data.json 12 | tox runserver 13 | 14 | And get access on http://127.0.0.1:8000 15 | 16 | 17 | * HelloWorld_ - Viewflow introduction sample 18 | * Shipment_ - Automate shipment process for ecommerce website 19 | * `Dynamic split`_ - Custom flow node example 20 | 21 | .. _HelloWorld: helloworld/ 22 | .. _Shipment: shipment/ 23 | .. _`Dynamic split`: customnode/ 24 | 25 | 26 | Standalone demo projects 27 | ======================== 28 | 29 | * Ecommerce_ - Delivery department automation with viewflow and Django Cartridge Ecommerce 30 | * Customization_ - Custom auth model and django-guardian for object level permissions, no viewsite 31 | 32 | .. _Ecommerce: https://github.com/kmmbvnr/viewflow-demo/tree/master/ecommerce 33 | .. _Customization: https://github.com/kmmbvnr/viewflow-demo/tree/master/customauth 34 | -------------------------------------------------------------------------------- /demo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/__init__.py -------------------------------------------------------------------------------- /demo/bloodtest/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/bloodtest/__init__.py -------------------------------------------------------------------------------- /demo/bloodtest/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import * 3 | # Register your models here. 4 | @admin.register(Patient) 5 | class PatientAdmin(admin.ModelAdmin): 6 | pass 7 | 8 | @admin.register(BloodTestProcess) 9 | class BloodTestProcessAdmin(admin.ModelAdmin): 10 | pass -------------------------------------------------------------------------------- /demo/bloodtest/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BloodtestConfig(AppConfig): 5 | name = 'bloodtest' 6 | -------------------------------------------------------------------------------- /demo/bloodtest/flows.py: -------------------------------------------------------------------------------- 1 | from viewflow import flow, frontend 2 | from viewflow.base import this, Flow 3 | 4 | from . import views, models 5 | 6 | 7 | @frontend.register 8 | class BloodTestFlow(Flow): 9 | process_class = models.BloodTestProcess 10 | 11 | first_sample = flow.Start( 12 | views.FirstBoodSampleView 13 | ).Next(this.biochemical_analysis) 14 | 15 | second_sample = flow.Start( 16 | views.second_blood_sample 17 | ).Next(this.biochemical_analysis) 18 | 19 | biochemical_analysis = flow.View( 20 | views.biochemical_data 21 | ).Next(this.split_analysis) 22 | 23 | split_analysis = ( 24 | flow.Split() 25 | .Next( 26 | this.hormone_tests, 27 | cond=lambda act: act.process.hormone_test_required 28 | ).Next( 29 | this.tumor_markers_test, 30 | cond=lambda act: act.process.tumor_test_required 31 | ).Next( 32 | this.join_analysis 33 | ) 34 | ) 35 | 36 | hormone_tests = flow.View( 37 | views.HormoneTestFormView 38 | ).Next(this.join_analysis) 39 | 40 | tumor_markers_test = flow.View( 41 | views.GenericTestFormView, 42 | model=models.TumorMarkers, 43 | fields=[ 44 | 'alpha_fetoprotein', 'beta_gonadotropin', 'ca19', 45 | 'cea', 'pap', 'pas' 46 | ], 47 | task_description='Tumor Markers Test' 48 | ).Next(this.join_analysis) 49 | 50 | join_analysis = flow.Join().Next(this.end) 51 | 52 | end = flow.End() 53 | -------------------------------------------------------------------------------- /demo/bloodtest/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from material import Layout, Row, Span2 3 | 4 | from . import models 5 | 6 | 7 | class PatientForm(forms.ModelForm): 8 | layout = Layout( 9 | Row(Span2('patient_id'), 'age', 'sex'), 10 | Row('weight', 'height'), 11 | 'comment', 12 | ) 13 | 14 | class Meta: 15 | model = models.Patient 16 | fields = '__all__' 17 | 18 | 19 | class BloodSampleForm(forms.ModelForm): 20 | class Meta: 21 | model = models.BloodSample 22 | fields = ['taken_at', 'notes'] 23 | 24 | 25 | class BiochemistryForm(forms.ModelForm): 26 | class Meta: 27 | model = models.Biochemistry 28 | fields = ['hemoglobin', 'lymphocytes'] 29 | 30 | 31 | class SecondBloodSampleForm(BloodSampleForm): 32 | patient = forms.ModelChoiceField(queryset=models.Patient.objects.all()) 33 | -------------------------------------------------------------------------------- /demo/bloodtest/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.7 on 2017-11-22 08:56 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | import django.utils.timezone 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | initial = True 14 | 15 | dependencies = [ 16 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 17 | ('viewflow', '0006_i18n'), 18 | ] 19 | 20 | operations = [ 21 | migrations.CreateModel( 22 | name='Biochemistry', 23 | fields=[ 24 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 25 | ('hemoglobin', models.IntegerField(help_text='g/dL')), 26 | ('lymphocytes', models.DecimalField(decimal_places=1, help_text='10^9/L', max_digits=3)), 27 | ], 28 | ), 29 | migrations.CreateModel( 30 | name='BloodSample', 31 | fields=[ 32 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 33 | ('taken_at', models.DateTimeField(default=django.utils.timezone.now)), 34 | ('notes', models.TextField(blank=True)), 35 | ], 36 | ), 37 | migrations.CreateModel( 38 | name='BloodTestProcess', 39 | fields=[ 40 | ('process_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='viewflow.Process')), 41 | ('sample', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='bloodtest.BloodSample')), 42 | ], 43 | options={ 44 | 'abstract': False, 45 | }, 46 | bases=('viewflow.process',), 47 | ), 48 | migrations.CreateModel( 49 | name='Hormones', 50 | fields=[ 51 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 52 | ('acth', models.DecimalField(decimal_places=1, help_text='pmol/L', max_digits=3)), 53 | ('estradiol', models.IntegerField(help_text='ng/dL')), 54 | ('free_t3', models.DecimalField(decimal_places=1, help_text='ng/dL', max_digits=3)), 55 | ('free_t4', models.IntegerField(help_text='pmol/L')), 56 | ('sample', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='bloodtest.BloodSample')), 57 | ], 58 | ), 59 | migrations.CreateModel( 60 | name='Patient', 61 | fields=[ 62 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 63 | ('patient_id', models.CharField(max_length=250)), 64 | ('age', models.IntegerField()), 65 | ('sex', models.CharField(choices=[('M', 'Male'), ('F', 'Female'), ('O', 'Other'), ('U', 'Unknown')], max_length=1)), 66 | ('weight', models.DecimalField(decimal_places=1, help_text='kg', max_digits=4)), 67 | ('height', models.IntegerField(help_text='cm')), 68 | ('comment', models.TextField(blank=True)), 69 | ], 70 | ), 71 | migrations.CreateModel( 72 | name='TumorMarkers', 73 | fields=[ 74 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 75 | ('alpha_fetoprotein', models.IntegerField(help_text='ng/mL')), 76 | ('beta_gonadotropin', models.IntegerField(help_text='IU/I')), 77 | ('ca19', models.IntegerField(help_text='U/mL')), 78 | ('cea', models.IntegerField(help_text='ug/L')), 79 | ('pap', models.IntegerField(help_text='U/dL')), 80 | ('pas', models.IntegerField(help_text='ug/L')), 81 | ('sample', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='bloodtest.BloodSample')), 82 | ], 83 | ), 84 | migrations.AddField( 85 | model_name='bloodsample', 86 | name='patient', 87 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='bloodtest.Patient'), 88 | ), 89 | migrations.AddField( 90 | model_name='bloodsample', 91 | name='taken_by', 92 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 93 | ), 94 | migrations.AddField( 95 | model_name='biochemistry', 96 | name='sample', 97 | field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='bloodtest.BloodSample'), 98 | ), 99 | ] 100 | -------------------------------------------------------------------------------- /demo/bloodtest/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/bloodtest/migrations/__init__.py -------------------------------------------------------------------------------- /demo/bloodtest/models.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from django.db import models 3 | from django.utils import timezone 4 | from viewflow.models import Process 5 | 6 | 7 | class Patient(models.Model): 8 | patient_id = models.CharField(max_length=250) 9 | age = models.IntegerField() 10 | sex = models.CharField( 11 | max_length=1, 12 | choices=( 13 | ("M", "Male"), 14 | ("F", "Female"), 15 | ("O", "Other"), 16 | ("U", "Unknown"))) 17 | weight = models.DecimalField( 18 | max_digits=4, decimal_places=1, 19 | help_text='kg') 20 | height = models.IntegerField(help_text="cm") 21 | comment = models.TextField(blank=True) 22 | 23 | def __str__(self): 24 | return self.patient_id 25 | 26 | 27 | class BloodSample(models.Model): 28 | patient = models.ForeignKey(Patient) 29 | taken_at = models.DateTimeField(default=timezone.now) 30 | taken_by = models.ForeignKey(User) 31 | notes = models.TextField(blank=True) 32 | 33 | 34 | class Biochemistry(models.Model): 35 | sample = models.OneToOneField(BloodSample) 36 | hemoglobin = models.IntegerField(help_text='g/dL') 37 | lymphocytes = models.DecimalField(max_digits=3, decimal_places=1, help_text='10^9/L') 38 | 39 | 40 | class TumorMarkers(models.Model): 41 | sample = models.OneToOneField(BloodSample) 42 | alpha_fetoprotein = models.IntegerField(help_text='ng/mL') 43 | beta_gonadotropin = models.IntegerField(help_text='IU/I') 44 | ca19 = models.IntegerField(help_text='U/mL') 45 | cea = models.IntegerField(help_text='ug/L') 46 | pap = models.IntegerField(help_text='U/dL') 47 | pas = models.IntegerField(help_text='ug/L') 48 | 49 | 50 | class Hormones(models.Model): 51 | sample = models.OneToOneField(BloodSample) 52 | acth = models.DecimalField(max_digits=3, decimal_places=1, help_text='pmol/L') 53 | estradiol = models.IntegerField(help_text='ng/dL') 54 | free_t3 = models.DecimalField(max_digits=3, decimal_places=1, help_text='ng/dL') 55 | free_t4 = models.IntegerField(help_text='pmol/L') 56 | 57 | 58 | class BloodTestProcess(Process): 59 | sample = models.ForeignKey(BloodSample) 60 | 61 | @property 62 | def tumor_test_required(self): 63 | return self.sample.biochemistry.lymphocytes > 5 64 | 65 | @property 66 | def hormone_test_required(self): 67 | return self.sample.biochemistry.hemoglobin < 12 68 | -------------------------------------------------------------------------------- /demo/bloodtest/templates/bloodtest/bloodtest/biochemical_data.html: -------------------------------------------------------------------------------- 1 | {% extends 'viewflow/flow/task.html' %} 2 | -------------------------------------------------------------------------------- /demo/bloodtest/templates/bloodtest/bloodtest/first_sample.html: -------------------------------------------------------------------------------- 1 | {% extends 'viewflow/flow/start.html' %} 2 | {% load viewflow material_form material_frontend %} 3 | 4 | {% block left-panel %} 5 |
6 |
7 |
8 |
9 |
{{ activation.flow_task.task_title|default:activation.task.flow_task }}
10 | Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }} 11 | 12 | {% csrf_token %} 13 | 14 | {{ wizard.management_form }} 15 | {% form form=wizard.form %}{% endform %} 16 | {{ activation.management_form }} 17 |
18 |
19 |
20 | {% if wizard.steps.prev %} 21 | 22 | {% endif %} 23 | {% if wizard.steps.next %} 24 | 25 | {% else %} 26 | 27 | {% endif %} 28 |
29 |
30 |
31 |
32 |
33 | {% endblock %} 34 | -------------------------------------------------------------------------------- /demo/bloodtest/templates/bloodtest/bloodtest/hormone_data.html: -------------------------------------------------------------------------------- 1 | {% extends 'viewflow/flow/task.html' %} 2 | -------------------------------------------------------------------------------- /demo/bloodtest/templates/bloodtest/bloodtest/second_sample.html: -------------------------------------------------------------------------------- 1 | {% extends 'viewflow/flow/start.html' %} 2 | -------------------------------------------------------------------------------- /demo/bloodtest/views.py: -------------------------------------------------------------------------------- 1 | from django.views import generic 2 | from django.shortcuts import render, redirect 3 | from formtools.wizard.views import SessionWizardView 4 | 5 | from viewflow.decorators import flow_start_view, flow_view 6 | from viewflow.flow.views import StartFlowMixin, FlowMixin 7 | from viewflow.flow.views.utils import get_next_task_url 8 | 9 | from . import forms, models 10 | 11 | 12 | class FirstBoodSampleView(StartFlowMixin, SessionWizardView): 13 | template_name = 'bloodtest/bloodtest/first_sample.html' 14 | 15 | form_list = [forms.PatientForm, forms.BloodSampleForm] 16 | 17 | def done(self, form_list, form_dict, **kwargs): 18 | patient = form_dict['0'].save() 19 | 20 | sample = form_dict['1'].save(commit=False) 21 | sample.patient = patient 22 | sample.taken_by = self.request.user 23 | sample.save() 24 | 25 | self.activation.process.sample = sample 26 | self.activation.done() 27 | 28 | return redirect(get_next_task_url(self.request, self.activation.process)) 29 | 30 | 31 | @flow_start_view 32 | def second_blood_sample(request, **kwargs): 33 | request.activation.prepare(request.POST or None, user=request.user) 34 | form = forms.SecondBloodSampleForm(request.POST or None) 35 | 36 | if form.is_valid(): 37 | sample = form.save(commit=False) 38 | sample.patient = form.cleaned_data['patient'] 39 | sample.taken_by = request.user 40 | sample.save() 41 | 42 | request.activation.process.sample = sample 43 | request.activation.done() 44 | 45 | return redirect(get_next_task_url(request, request.activation.process)) 46 | 47 | return render(request, 'bloodtest/bloodtest/second_sample.html', { 48 | 'form': form, 49 | 'activation': request.activation 50 | }) 51 | 52 | 53 | @flow_view 54 | def biochemical_data(request, **kwargs): 55 | request.activation.prepare(request.POST or None, user=request.user) 56 | form = forms.BiochemistryForm(request.POST or None) 57 | 58 | if form.is_valid(): 59 | biochemestry = form.save(commit=False) 60 | biochemestry.sample = request.activation.process.sample 61 | biochemestry.save() 62 | request.activation.done() 63 | return redirect(get_next_task_url(request, request.activation.process)) 64 | 65 | return render(request, 'bloodtest/bloodtest/biochemical_data.html', { 66 | 'form': form, 67 | 'activation': request.activation 68 | }) 69 | 70 | 71 | class HormoneTestFormView(FlowMixin, generic.CreateView): 72 | template_name = 'bloodtest/bloodtest/hormone_data.html' 73 | model = models.Hormones 74 | fields = [ 75 | 'acth', 'estradiol', 'free_t3', 'free_t4' 76 | ] 77 | 78 | def form_valid(self, form): 79 | hormone_data = form.save(commit=False) 80 | hormone_data.sample = self.activation.process.sample 81 | hormone_data.save() 82 | self.activation_done() 83 | return redirect(self.get_success_url()) 84 | 85 | 86 | class GenericTestFormView(FlowMixin, generic.CreateView): 87 | """A generic view to save blood test data. 88 | 89 | Assumes that test data model have FK `sample` field. The view can 90 | be parametrized directly in flow definition. 91 | 92 | """ 93 | def form_valid(self, form): 94 | test_data = form.save(commit=False) 95 | test_data.sample = self.activation.process.sample 96 | test_data.save() 97 | self.activation_done() 98 | return redirect(self.get_success_url()) 99 | -------------------------------------------------------------------------------- /demo/countersign/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/countersign/__init__.py -------------------------------------------------------------------------------- /demo/countersign/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import * 3 | # Register your models here. 4 | @admin.register(cs_process) 5 | class CSAdmin(admin.ModelAdmin): 6 | list_display = ( 'version', 'mark') -------------------------------------------------------------------------------- /demo/countersign/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CountersignConfig(AppConfig): 5 | icon = 'flag' 6 | name = 'demo.countersign' 7 | -------------------------------------------------------------------------------- /demo/countersign/flows.py: -------------------------------------------------------------------------------- 1 | import os 2 | from django.utils.translation import ugettext_lazy as _ 3 | 4 | from viewflow import flow, frontend, lock 5 | from viewflow.base import this, Flow 6 | from viewflow.flow import views as flow_views 7 | 8 | 9 | from .models import cs_process 10 | 11 | 12 | @frontend.register 13 | class CSFlow(Flow): 14 | """ 15 | 并行会签demo 16 | 动态拆分见作者自带的customnode列子。 17 | """ 18 | process_class = cs_process 19 | process_title = _('会签') 20 | process_description = _('会签演示.') 21 | 22 | lock_impl = lock.select_for_update_lock 23 | 24 | summary_template = _("'{{ process.mark }}' 会签") 25 | 26 | start = ( 27 | flow.Start( 28 | flow_views.CreateProcessView, 29 | fields=['file','mark','version'], 30 | task_title=_('新会签')) 31 | .Permission(auto_create=True) 32 | .Next(this.split_sign) 33 | ) 34 | 35 | split_sign=( 36 | flow.Split( 37 | task_title=_('并行会签') 38 | ) 39 | .Next(this.cfo_sign) 40 | .Next(this.ceo_sign) 41 | ) 42 | 43 | #财务官会签 44 | cfo_sign=( 45 | flow.View( 46 | flow_views.UpdateProcessView, fields=['file','mark','cfo_approved'], 47 | task_title=_('CFO 会签'), 48 | task_result_summary = _("CFO{% ifequal process.cfo_approved '1' %} 同意 {% else %}不同意{% endifequal %}") 49 | ) 50 | .Permission(auto_create=True) 51 | .Next(this.join_on_sign) 52 | ) 53 | 54 | #CEO会签 55 | ceo_sign=( 56 | flow.View( 57 | flow_views.UpdateProcessView, fields=['file','mark','ceo_approved'], 58 | task_title=_('CEO 会签'), 59 | task_result_summary = _("CEO{% if process.ceo_approved == '1' %} 同意 {% else %}不同意{% endif %}") 60 | ) 61 | .Permission(auto_create=True) 62 | .Next(this.join_on_sign) 63 | ) 64 | 65 | join_on_sign = ( 66 | flow.Join( 67 | task_title=_('等待所有会签结束') 68 | ).Next(this.decision) 69 | ) 70 | #一票否决制。 71 | decision = ( 72 | flow.If( 73 | cond=lambda act: act.process.cfo_approved=='1' and act.process.ceo_approved=='1', 74 | task_title=_('会签结果'), 75 | task_result_summary=_("会签结果{% if process.ceo_approved == '1' and process.cfo_approved == '1'%} 通过 {% else %}不通过{% endif %}") 76 | ) 77 | .Then(this.OK) 78 | .Else(this.NG) 79 | ) 80 | 81 | NG = ( 82 | flow.Handler( 83 | this.send_NG_request 84 | ).Next(this.end) 85 | ) 86 | 87 | OK = ( 88 | flow.Handler( 89 | this.send_OK_request 90 | ).Next(this.end) 91 | ) 92 | 93 | end = flow.End() 94 | 95 | def send_NG_request(self, activation): 96 | print('ceo_approved==>'+ str(activation.process.ceo_approved)) 97 | print('cfo_approved==>' + str(activation.process.cfo_approved)) 98 | print('NG') 99 | 100 | def send_OK_request(self, activation): 101 | print('OK') -------------------------------------------------------------------------------- /demo/countersign/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.7 on 2017-11-27 06:17 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ('viewflow', '0006_i18n'), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='cs_process', 20 | fields=[ 21 | ('process_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='viewflow.Process')), 22 | ('version', models.CharField(max_length=50, verbose_name='版本')), 23 | ('mark', models.CharField(max_length=1024, verbose_name='备注')), 24 | ('cfo_approved', models.CharField(choices=[('1', '同意'), ('2', '不同意')], max_length=1)), 25 | ('ceo_approved', models.CharField(choices=[('1', '同意'), ('2', '不同意')], max_length=1)), 26 | ], 27 | options={ 28 | 'verbose_name': '会签', 29 | 'verbose_name_plural': '会签', 30 | }, 31 | bases=('viewflow.process',), 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /demo/countersign/migrations/0002_auto_20171127_1554.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.7 on 2017-11-27 07:54 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('countersign', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='cs_process', 17 | name='file', 18 | field=models.FileField(default='blank', upload_to='./upload/', verbose_name='会签文件'), 19 | preserve_default=False, 20 | ), 21 | migrations.AlterField( 22 | model_name='cs_process', 23 | name='ceo_approved', 24 | field=models.CharField(choices=[('1', '同意'), ('0', '不同意')], max_length=1), 25 | ), 26 | migrations.AlterField( 27 | model_name='cs_process', 28 | name='cfo_approved', 29 | field=models.CharField(choices=[('1', '同意'), ('0', '不同意')], max_length=1), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /demo/countersign/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/countersign/migrations/__init__.py -------------------------------------------------------------------------------- /demo/countersign/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from viewflow.models import Process 3 | from django.utils.translation import ugettext_lazy as _ 4 | # Create your models here. 5 | 6 | class cs_process(Process): 7 | #能上传,但不支持下载,可以自定义FORM模板下载 8 | file=models.FileField(_('会签文件'), upload_to = './upload/') 9 | version = models.CharField(_('版本'), max_length=50) 10 | mark = models.CharField(_('备注'), max_length=1024) 11 | 12 | cfo_approved = models.CharField( 13 | max_length=1, 14 | choices=( 15 | ("1", "同意"), 16 | ("0", "不同意"),)) 17 | 18 | ceo_approved = models.CharField( 19 | max_length=1, 20 | choices=( 21 | ("1", "同意"), 22 | ("0", "不同意"),)) 23 | 24 | class Meta: 25 | verbose_name = _("会签") 26 | verbose_name_plural = _('会签') -------------------------------------------------------------------------------- /demo/countersign/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /demo/customnode/README.rst: -------------------------------------------------------------------------------- 1 | ================== 2 | Extending Viewflow 3 | ================== 4 | 5 | http://demo.viewflow.io/workflow/dynamicsplit/ 6 | 7 | In this process we are collecting responses (yes, no) from different users and Join that. 8 | Required users count determined in the first step. 9 | 10 | .. image:: doc/DynamicSplit.png 11 | :width: 400px 12 | 13 | `nodes.DynamicSplit`_ - Custom dynamic split node example 14 | 15 | .. _`nodes.DynamicSplit`: nodes.py 16 | 17 | -------------------------------------------------------------------------------- /demo/customnode/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/customnode/__init__.py -------------------------------------------------------------------------------- /demo/customnode/doc/DynamicSplit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/customnode/doc/DynamicSplit.png -------------------------------------------------------------------------------- /demo/customnode/flows.py: -------------------------------------------------------------------------------- 1 | from viewflow import flow, frontend 2 | from viewflow.base import this, Flow 3 | from viewflow.flow.views import CreateProcessView 4 | 5 | from . import models, views 6 | from .nodes import DynamicSplit 7 | 8 | 9 | class DynamicSplitFlow(Flow): 10 | """ 11 | Dynamic split 12 | 13 | Depends on initial decision, several instances on make_decision task would be instantiated 14 | """ 15 | process_class = models.DynamicSplitProcess 16 | 17 | summary_template = """ 18 | Decision on: {{ process.question }}
19 | {{ process.decision_set.count }} of {{ process.split_count }} completed 20 | """ 21 | 22 | start = ( 23 | flow.Start( 24 | CreateProcessView, 25 | fields=['question', 'split_count'], 26 | task_result_summary="Asks for {{ process.split_count }} decisions") 27 | .Permission(auto_create=True) 28 | .Next(this.spit_on_decision) 29 | ) 30 | 31 | spit_on_decision = ( 32 | DynamicSplit(lambda p: p.split_count) 33 | .IfNone(this.end) 34 | .Next(this.make_decision) 35 | ) 36 | 37 | make_decision = ( 38 | flow.View( 39 | views.DecisionView, 40 | task_description="Decision required") 41 | .Next(this.join_on_decision) 42 | ) 43 | 44 | join_on_decision = flow.Join().Next(this.end) 45 | 46 | end = flow.End() 47 | 48 | frontend.register(DynamicSplitFlow) 49 | -------------------------------------------------------------------------------- /demo/customnode/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.7 on 2017-11-22 08:56 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | initial = True 13 | 14 | dependencies = [ 15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 16 | ('viewflow', '0006_i18n'), 17 | ] 18 | 19 | operations = [ 20 | migrations.CreateModel( 21 | name='Decision', 22 | fields=[ 23 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 24 | ('decision', models.BooleanField(default=False)), 25 | ], 26 | ), 27 | migrations.CreateModel( 28 | name='DynamicSplitProcess', 29 | fields=[ 30 | ('process_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='viewflow.Process')), 31 | ('question', models.CharField(max_length=50)), 32 | ('split_count', models.IntegerField(default=0)), 33 | ], 34 | options={ 35 | 'abstract': False, 36 | }, 37 | bases=('viewflow.process',), 38 | ), 39 | migrations.AddField( 40 | model_name='decision', 41 | name='process', 42 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='customnode.DynamicSplitProcess'), 43 | ), 44 | migrations.AddField( 45 | model_name='decision', 46 | name='user', 47 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), 48 | ), 49 | ] 50 | -------------------------------------------------------------------------------- /demo/customnode/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/customnode/migrations/__init__.py -------------------------------------------------------------------------------- /demo/customnode/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.conf import settings 3 | from viewflow.models import Process 4 | 5 | 6 | class DynamicSplitProcess(Process): 7 | question = models.CharField(max_length=50) 8 | split_count = models.IntegerField(default=0) 9 | 10 | 11 | class Decision(models.Model): 12 | process = models.ForeignKey(DynamicSplitProcess, on_delete=models.CASCADE) 13 | user = models.ForeignKey( 14 | settings.AUTH_USER_MODEL, blank=True, null=True, on_delete=models.CASCADE) 15 | decision = models.BooleanField(default=False) 16 | -------------------------------------------------------------------------------- /demo/customnode/nodes.py: -------------------------------------------------------------------------------- 1 | from copy import copy 2 | 3 | from viewflow import Gateway, mixins 4 | from viewflow.exceptions import FlowRuntimeError 5 | from viewflow.activation import AbstractGateActivation 6 | from viewflow.token import Token 7 | from viewflow.flow import views 8 | 9 | 10 | class DynamicSplitActivation(AbstractGateActivation): 11 | def calculate_next(self): 12 | self._split_count = self.flow_task._task_count_callback(self.process) 13 | 14 | def activate_next(self): 15 | if self._split_count: 16 | token_source = Token.split_token_source(self.task.token, self.task.pk) 17 | for _ in range(self._split_count): 18 | self.flow_task._next.activate(prev_activation=self, token=next(token_source)) 19 | elif self.flow_task._ifnone_next_node is not None: 20 | self.flow_task._ifnone_next_node.activate(prev_activation=self, token=self.task.token) 21 | else: 22 | raise FlowRuntimeError("{} activated with zero and no IfNone nodes specified".format(self.flow_task.name)) 23 | 24 | 25 | class DynamicSplit(mixins.NextNodeMixin, 26 | mixins.UndoViewMixin, 27 | mixins.CancelViewMixin, 28 | mixins.PerformViewMixin, 29 | mixins.DetailViewMixin, 30 | Gateway): 31 | """ 32 | Activates several outgoing task instances depends on callback value 33 | 34 | Example:: 35 | 36 | spit_on_decision = flow.DynamicSplit(lambda p: 4) \\ 37 | .Next(this.make_decision) 38 | 39 | make_decision = flow.View(MyView) \\ 40 | .Next(this.join_on_decision) 41 | 42 | join_on_decision = flow.Join() \\ 43 | .Next(this.end) 44 | """ 45 | task_type = 'SPLIT' 46 | 47 | cancel_view_class = views.CancelTaskView 48 | detail_view_class = views.DetailTaskView 49 | perform_view_class = views.PerformTaskView 50 | undo_view_class = views.UndoTaskView 51 | 52 | activation_class = DynamicSplitActivation 53 | 54 | def __init__(self, callback): 55 | super(DynamicSplit, self).__init__() 56 | self._task_count_callback = callback 57 | self._ifnone_next_node = None 58 | 59 | def _resolve(self, resolver): 60 | super(DynamicSplit, self)._resolve(resolver) 61 | self._ifnone_next_node = resolver.get_implementation(self._ifnone_next_node) 62 | 63 | def IfNone(self, node): 64 | result = copy(self) 65 | result._ifnone_next_node = node 66 | return result 67 | -------------------------------------------------------------------------------- /demo/customnode/urls.py: -------------------------------------------------------------------------------- 1 | from viewflow.flow.viewset import FlowViewSet 2 | from .flows import DynamicSplitFlow 3 | 4 | 5 | urlpatterns = FlowViewSet(DynamicSplitFlow).urls 6 | -------------------------------------------------------------------------------- /demo/customnode/views.py: -------------------------------------------------------------------------------- 1 | from django.views import generic 2 | from django.http import HttpResponseRedirect 3 | 4 | from viewflow.flow.views import FlowMixin 5 | 6 | from . import models 7 | 8 | 9 | class DecisionView(FlowMixin, generic.CreateView): 10 | model = models.Decision 11 | fields = ['decision'] 12 | 13 | def form_valid(self, form): 14 | self.object = form.save(commit=False) 15 | 16 | self.object.user = self.request.user 17 | self.object.process = self.activation.process 18 | self.object.save() 19 | 20 | self.activation.done() 21 | self.success('Task {task} has been completed.') 22 | 23 | return HttpResponseRedirect(self.get_success_url()) 24 | -------------------------------------------------------------------------------- /demo/employees/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'demo.employees.apps.EmployeesConfig' -------------------------------------------------------------------------------- /demo/employees/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from . import models 4 | 5 | 6 | @admin.register(models.Department) 7 | class DepartmentAdmin(admin.ModelAdmin): 8 | icon = 'account_balance' 9 | list_display = ('dept_no', 'dept_name') 10 | 11 | 12 | @admin.register(models.DeptEmp) 13 | class DeptEmpAdmin(admin.ModelAdmin): 14 | icon = 'people_outline' 15 | list_display = ('employee', 'department', 'from_date', 'to_date') 16 | list_select_related = True 17 | raw_id_fields = ('employee', ) 18 | list_filter = ('department', ) 19 | 20 | 21 | @admin.register(models.DeptManager) 22 | class DeptManagerAdmin(admin.ModelAdmin): 23 | icon = 'assignment_ind' 24 | list_display = ('employee', 'department', 'from_date', 'to_date') 25 | raw_id_fields = ('employee', ) 26 | list_filter = ('department', ) 27 | 28 | 29 | @admin.register(models.Employee) 30 | class EmployeeAdmin(admin.ModelAdmin): 31 | icon = 'account_box' 32 | list_display = ('emp_no', 'first_name', 'last_name', 'hire_date') 33 | 34 | 35 | @admin.register(models.Salary) 36 | class SalaryAdmin(admin.ModelAdmin): 37 | icon = 'account_balance_wallet' 38 | list_display = ('employee', 'from_date', 'to_date', 'salary') 39 | raw_id_fields = ('employee', ) 40 | 41 | 42 | @admin.register(models.Title) 43 | class TitleAdmin(admin.ModelAdmin): 44 | icon = 'reorder' 45 | list_display = ('employee', 'from_date', 'to_date', 'title') 46 | raw_id_fields = ('employee', ) 47 | list_filter = ('title', ) 48 | -------------------------------------------------------------------------------- /demo/employees/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from django.utils.translation import ugettext_lazy as _ 3 | 4 | from material.frontend.apps import ModuleMixin 5 | 6 | 7 | class EmployeesConfig(ModuleMixin, AppConfig): 8 | name = 'demo.employees' 9 | icon = 'people' 10 | verbose_name = _('Employees') 11 | 12 | def has_perm(self, user): 13 | return user.is_superuser 14 | -------------------------------------------------------------------------------- /demo/employees/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.utils.translation import ugettext_lazy as _ 3 | 4 | from .models import Employee, DeptManager, Title, Salary 5 | 6 | 7 | class ChangeManagerForm(forms.Form): 8 | manager = forms.ModelChoiceField(queryset=Employee.objects.all()[:100], label=_('Manager')) 9 | 10 | def __init__(self, *args, **kwargs): 11 | self.department = kwargs.pop('department') 12 | super(ChangeManagerForm, self).__init__(*args, **kwargs) 13 | 14 | def save(self): 15 | new_manager = self.cleaned_data['manager'] 16 | 17 | DeptManager.objects.filter( 18 | department=self.department 19 | ).set( 20 | department=self.department, 21 | employee=new_manager 22 | ) 23 | 24 | 25 | class ChangeTitleForm(forms.Form): 26 | position = forms.CharField(label=_('Position')) 27 | 28 | def __init__(self, *args, **kwargs): 29 | self.employee = kwargs.pop('employee') 30 | super(ChangeTitleForm, self).__init__(*args, **kwargs) 31 | 32 | def save(self): 33 | new_title = self.cleaned_data['position'] 34 | 35 | Title.objects.filter( 36 | employee=self.employee, 37 | ).set( 38 | employee=self.employee, 39 | title=new_title 40 | ) 41 | 42 | 43 | class ChangeSalaryForm(forms.Form): 44 | salary = forms.IntegerField(max_value=1000000, label=_('Salary')) 45 | 46 | def __init__(self, *args, **kwargs): 47 | self.employee = kwargs.pop('employee') 48 | super(ChangeSalaryForm, self).__init__(*args, **kwargs) 49 | 50 | def save(self): 51 | new_salary = self.cleaned_data['salary'] 52 | 53 | Salary.objects.filter( 54 | employee=self.employee, 55 | ).set( 56 | employee=self.employee, 57 | salary=new_salary, 58 | ) 59 | -------------------------------------------------------------------------------- /demo/employees/locale/de/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/employees/locale/de/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /demo/employees/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # distributed under the same license as the Django Material package. 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: django-material\n" 5 | "Report-Msgid-Bugs-To: \n" 6 | "POT-Creation-Date: 2017-05-09 09:52+0000\n" 7 | "Language: de\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 12 | 13 | #: apps.py:10 templates/employees/department_employees.html:7 14 | #: templates/employees/menu.html:4 15 | msgid "Employees" 16 | msgstr "Mitarbeiter" 17 | 18 | #: forms.py:8 19 | msgid "manager" 20 | msgstr "" 21 | 22 | #: forms.py:26 23 | msgid "position" 24 | msgstr "" 25 | 26 | #: forms.py:44 models.py:76 models.py:85 27 | msgid "salary" 28 | msgstr "gehalt" 29 | 30 | #: models.py:11 31 | msgid "code" 32 | msgstr "" 33 | 34 | #: models.py:12 35 | msgid "name" 36 | msgstr "" 37 | 38 | #: models.py:15 models.py:27 models.py:42 39 | msgid "department" 40 | msgstr "abteilung" 41 | 42 | #: models.py:16 43 | msgid "departments" 44 | msgstr "Abteilungen" 45 | 46 | #: models.py:26 models.py:41 models.py:65 models.py:75 models.py:91 47 | msgid "employee" 48 | msgstr "mitarbeiter" 49 | 50 | #: models.py:28 models.py:43 models.py:77 models.py:93 51 | msgid "from" 52 | msgstr "aus" 53 | 54 | #: models.py:29 models.py:44 models.py:78 models.py:94 55 | msgid "to" 56 | msgstr "bis" 57 | 58 | #: models.py:34 59 | msgid "department employee" 60 | msgstr "abteilungsmitarbeiter" 61 | 62 | #: models.py:35 63 | msgid "department employees" 64 | msgstr "abteilungsmitarbeiter" 65 | 66 | #: models.py:49 67 | msgid "department manager" 68 | msgstr "abteilungsleiter" 69 | 70 | #: models.py:50 71 | msgid "department managers" 72 | msgstr "abteilungsleiter" 73 | 74 | #: models.py:57 75 | msgid "employee number" 76 | msgstr "mitarbeiternummer" 77 | 78 | #: models.py:58 79 | msgid "birthday" 80 | msgstr "geburtstag" 81 | 82 | #: models.py:59 83 | msgid "first name" 84 | msgstr "vorname" 85 | 86 | #: models.py:60 87 | msgid "last name" 88 | msgstr "nachname" 89 | 90 | #: models.py:61 91 | msgid "gender" 92 | msgstr "geschlecht" 93 | 94 | #: models.py:62 95 | msgid "hire date" 96 | msgstr "anstellungsdatum" 97 | 98 | #: models.py:66 views.py:137 99 | msgid "employees" 100 | msgstr "mitarbeiter" 101 | 102 | #: models.py:86 103 | msgid "salaries" 104 | msgstr "gehälter" 105 | 106 | #: models.py:92 models.py:99 107 | msgid "title" 108 | msgstr "titel" 109 | 110 | #: models.py:100 111 | msgid "titles" 112 | msgstr "titeln" 113 | 114 | #: templates/employees/change_manager.html:7 115 | msgid "Change Manager" 116 | msgstr "manager ändern" 117 | 118 | #: templates/employees/change_salary.html:7 119 | msgid "Change Salary" 120 | msgstr "gehalt ändern" 121 | 122 | #: templates/employees/change_salary.html:14 views.py:39 123 | msgid "Salary History" 124 | msgstr "Gehaltsgeschichte" 125 | 126 | #: templates/employees/change_salary.html:43 127 | msgid "Set new Salary" 128 | msgstr "Neues Gehalt setzen" 129 | 130 | #: templates/employees/change_title.html:7 131 | msgid "Change Title" 132 | msgstr "Titel ändern" 133 | 134 | #: templates/employees/change_title.html:16 135 | #: templates/employees/employee_detail.html:29 136 | msgid "Positions" 137 | msgstr "Positionen" 138 | 139 | #: templates/employees/change_title.html:20 140 | #: templates/employees/employee_detail.html:33 141 | msgid "Position" 142 | msgstr "Position" 143 | 144 | #: templates/employees/change_title.html:21 145 | #: templates/employees/department_detail.html:42 146 | #: templates/employees/employee_detail.html:34 147 | #: templates/employees/employee_detail.html:60 148 | #: templates/employees/employee_detail.html:85 149 | msgid "Since" 150 | msgstr "seit" 151 | 152 | #: templates/employees/change_title.html:22 153 | #: templates/employees/employee_detail.html:35 154 | #: templates/employees/employee_detail.html:61 155 | msgid "To" 156 | msgstr "Bis" 157 | 158 | #: templates/employees/change_title.html:38 159 | #: templates/employees/department_detail.html:29 160 | #: templates/employees/department_detail.html:56 161 | #: templates/employees/employee_detail.html:23 162 | #: templates/employees/employee_detail.html:50 163 | #: templates/employees/employee_detail.html:99 164 | msgid "Change" 165 | msgstr "Ändern" 166 | 167 | #: templates/employees/department_detail.html:28 168 | #: templates/employees/employee_detail.html:22 169 | msgid "Delete" 170 | msgstr "Löschen" 171 | 172 | #: templates/employees/department_detail.html:37 173 | msgid "Department Managers" 174 | msgstr "Abteilungsleiter" 175 | 176 | #: templates/employees/department_detail.html:41 177 | msgid "Manager" 178 | msgstr "" 179 | 180 | #: templates/employees/employee_detail.html:55 templates/employees/menu.html:3 181 | msgid "Departments" 182 | msgstr "Abteilungen" 183 | 184 | #: templates/employees/employee_detail.html:59 185 | msgid "Department" 186 | msgstr "Abteilung" 187 | 188 | #: templates/employees/employee_detail.html:80 189 | #: templates/employees/employee_detail.html:84 190 | msgid "Salary" 191 | msgstr "Gehalt" 192 | 193 | #: views.py:107 194 | msgid "current salary" 195 | msgstr "Aktuelles gehalt" 196 | -------------------------------------------------------------------------------- /demo/employees/locale/es/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/employees/locale/es/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /demo/employees/locale/fr/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/employees/locale/fr/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /demo/employees/locale/fr/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # distributed under the same license as the Django Material package. 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: django-material\n" 5 | "Report-Msgid-Bugs-To: \n" 6 | "POT-Creation-Date: 2017-05-09 09:52+0000\n" 7 | "Language: fr\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 12 | 13 | #: apps.py:10 templates/employees/department_employees.html:7 14 | #: templates/employees/menu.html:4 15 | msgid "Employees" 16 | msgstr "Employés" 17 | 18 | #: forms.py:8 19 | msgid "manager" 20 | msgstr "directeur" 21 | 22 | #: forms.py:26 23 | msgid "position" 24 | msgstr "titre" 25 | 26 | #: forms.py:44 models.py:76 models.py:85 27 | msgid "salary" 28 | msgstr "un salaire" 29 | 30 | #: models.py:11 31 | msgid "code" 32 | msgstr "" 33 | 34 | #: models.py:12 35 | msgid "name" 36 | msgstr "prénom" 37 | 38 | #: models.py:15 models.py:27 models.py:42 39 | msgid "department" 40 | msgstr "département" 41 | 42 | #: models.py:16 43 | msgid "departments" 44 | msgstr "départements" 45 | 46 | #: models.py:26 models.py:41 models.py:65 models.py:75 models.py:91 47 | msgid "employee" 48 | msgstr "employé" 49 | 50 | #: models.py:28 models.py:43 models.py:77 models.py:93 51 | msgid "from" 52 | msgstr "de" 53 | 54 | #: models.py:29 models.py:44 models.py:78 models.py:94 55 | msgid "to" 56 | msgstr "à" 57 | 58 | #: models.py:34 59 | msgid "department employee" 60 | msgstr "employé du département" 61 | 62 | #: models.py:35 63 | msgid "department employees" 64 | msgstr "employés du département" 65 | 66 | #: models.py:49 67 | msgid "department manager" 68 | msgstr "gestionnaire département" 69 | 70 | #: models.py:50 71 | msgid "department managers" 72 | msgstr "directeurs de département" 73 | 74 | #: models.py:57 75 | msgid "employee number" 76 | msgstr "numéro d'employé" 77 | 78 | #: models.py:58 79 | msgid "birthday" 80 | msgstr "anniversaire" 81 | 82 | #: models.py:59 83 | msgid "first name" 84 | msgstr "prénom" 85 | 86 | #: models.py:60 87 | msgid "last name" 88 | msgstr "nom de famille" 89 | 90 | #: models.py:61 91 | msgid "gender" 92 | msgstr "le genre" 93 | 94 | #: models.py:62 95 | msgid "hire date" 96 | msgstr "date d'embauche" 97 | 98 | #: models.py:66 views.py:137 99 | msgid "employees" 100 | msgstr "employés" 101 | 102 | #: models.py:86 103 | msgid "salaries" 104 | msgstr "les salaires" 105 | 106 | #: models.py:92 models.py:99 107 | msgid "title" 108 | msgstr "titre" 109 | 110 | #: models.py:100 111 | msgid "titles" 112 | msgstr "Titres" 113 | 114 | #: templates/employees/change_manager.html:7 115 | msgid "Change Manager" 116 | msgstr "Changer de directeur" 117 | 118 | #: templates/employees/change_salary.html:7 119 | msgid "Change Salary" 120 | msgstr "Changer de salaire" 121 | 122 | #: templates/employees/change_salary.html:14 views.py:39 123 | msgid "Salary History" 124 | msgstr "Historique des salaires" 125 | 126 | #: templates/employees/change_salary.html:43 127 | msgid "Set new Salary" 128 | msgstr "Nouveau salaire" 129 | 130 | #: templates/employees/change_title.html:7 131 | msgid "Change Title" 132 | msgstr "Modifier le titre" 133 | 134 | #: templates/employees/change_title.html:16 135 | #: templates/employees/employee_detail.html:29 136 | msgid "Positions" 137 | msgstr "Titres" 138 | 139 | #: templates/employees/change_title.html:20 140 | #: templates/employees/employee_detail.html:33 141 | msgid "Position" 142 | msgstr "Titre" 143 | 144 | #: templates/employees/change_title.html:21 145 | #: templates/employees/department_detail.html:42 146 | #: templates/employees/employee_detail.html:34 147 | #: templates/employees/employee_detail.html:60 148 | #: templates/employees/employee_detail.html:85 149 | msgid "Since" 150 | msgstr "Depuis" 151 | 152 | #: templates/employees/change_title.html:22 153 | #: templates/employees/employee_detail.html:35 154 | #: templates/employees/employee_detail.html:61 155 | msgid "To" 156 | msgstr "À" 157 | 158 | #: templates/employees/change_title.html:38 159 | #: templates/employees/department_detail.html:29 160 | #: templates/employees/department_detail.html:56 161 | #: templates/employees/employee_detail.html:23 162 | #: templates/employees/employee_detail.html:50 163 | #: templates/employees/employee_detail.html:99 164 | msgid "Change" 165 | msgstr "" 166 | 167 | #: templates/employees/department_detail.html:28 168 | #: templates/employees/employee_detail.html:22 169 | msgid "Delete" 170 | msgstr "" 171 | 172 | #: templates/employees/department_detail.html:37 173 | msgid "Department Managers" 174 | msgstr "Directeurs de département" 175 | 176 | #: templates/employees/department_detail.html:41 177 | msgid "Manager" 178 | msgstr "Directeur" 179 | 180 | #: templates/employees/employee_detail.html:55 templates/employees/menu.html:3 181 | msgid "Departments" 182 | msgstr "Départements" 183 | 184 | #: templates/employees/employee_detail.html:59 185 | msgid "Department" 186 | msgstr "Département" 187 | 188 | #: templates/employees/employee_detail.html:80 189 | #: templates/employees/employee_detail.html:84 190 | msgid "Salary" 191 | msgstr "Un salaire" 192 | 193 | #: views.py:107 194 | msgid "current salary" 195 | msgstr "salaire actuel" 196 | -------------------------------------------------------------------------------- /demo/employees/locale/ko/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/employees/locale/ko/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /demo/employees/locale/ko/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # distributed under the same license as the Django Material package. 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: django-material\n" 5 | "Report-Msgid-Bugs-To: \n" 6 | "POT-Creation-Date: 2017-05-09 09:52+0000\n" 7 | "Language: ko\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "Plural-Forms: nplurals=1; plural=0;\n" 12 | 13 | #: apps.py:10 templates/employees/department_employees.html:7 14 | #: templates/employees/menu.html:4 15 | msgid "Employees" 16 | msgstr "직원" 17 | 18 | #: forms.py:8 19 | msgid "manager" 20 | msgstr "매니저" 21 | 22 | #: forms.py:26 23 | msgid "position" 24 | msgstr "위치" 25 | 26 | #: forms.py:44 models.py:76 models.py:85 27 | msgid "salary" 28 | msgstr "봉급" 29 | 30 | #: models.py:11 31 | msgid "code" 32 | msgstr "암호" 33 | 34 | #: models.py:12 35 | msgid "name" 36 | msgstr "이름" 37 | 38 | #: models.py:15 models.py:27 models.py:42 39 | msgid "department" 40 | msgstr "학과" 41 | 42 | #: models.py:16 43 | msgid "departments" 44 | msgstr "부서" 45 | 46 | #: models.py:26 models.py:41 models.py:65 models.py:75 models.py:91 47 | msgid "employee" 48 | msgstr "종업원" 49 | 50 | #: models.py:28 models.py:43 models.py:77 models.py:93 51 | msgid "from" 52 | msgstr "부터" 53 | 54 | #: models.py:29 models.py:44 models.py:78 models.py:94 55 | msgid "to" 56 | msgstr "에" 57 | 58 | #: models.py:34 59 | msgid "department employee" 60 | msgstr "부서 직원" 61 | 62 | #: models.py:35 63 | msgid "department employees" 64 | msgstr "부서 직원" 65 | 66 | #: models.py:49 67 | msgid "department manager" 68 | msgstr "부서 관리자" 69 | 70 | #: models.py:50 71 | msgid "department managers" 72 | msgstr "부서 관리자" 73 | 74 | #: models.py:57 75 | msgid "employee number" 76 | msgstr "직원 번호" 77 | 78 | #: models.py:58 79 | msgid "birthday" 80 | msgstr "생일" 81 | 82 | #: models.py:59 83 | msgid "first name" 84 | msgstr "이름" 85 | 86 | #: models.py:60 87 | msgid "last name" 88 | msgstr "성" 89 | 90 | #: models.py:61 91 | msgid "gender" 92 | msgstr "성별" 93 | 94 | #: models.py:62 95 | msgid "hire date" 96 | msgstr "고용 날짜" 97 | 98 | #: models.py:66 views.py:137 99 | msgid "employees" 100 | msgstr "직원" 101 | 102 | #: models.py:86 103 | msgid "salaries" 104 | msgstr "급여" 105 | 106 | #: models.py:92 models.py:99 107 | msgid "title" 108 | msgstr "표제" 109 | 110 | #: models.py:100 111 | msgid "titles" 112 | msgstr "제목들" 113 | 114 | #: templates/employees/change_manager.html:7 115 | msgid "Change Manager" 116 | msgstr "변경 관리자" 117 | 118 | #: templates/employees/change_salary.html:7 119 | msgid "Change Salary" 120 | msgstr "연봉 변경" 121 | 122 | #: templates/employees/change_salary.html:14 views.py:39 123 | msgid "Salary History" 124 | msgstr "연봉 연혁" 125 | 126 | #: templates/employees/change_salary.html:43 127 | msgid "Set new Salary" 128 | msgstr "새 급여" 129 | 130 | #: templates/employees/change_title.html:7 131 | msgid "Change Title" 132 | msgstr "제목 변경" 133 | 134 | #: templates/employees/change_title.html:16 135 | #: templates/employees/employee_detail.html:29 136 | msgid "Positions" 137 | msgstr "직책" 138 | 139 | #: templates/employees/change_title.html:20 140 | #: templates/employees/employee_detail.html:33 141 | msgid "Position" 142 | msgstr "위치" 143 | 144 | #: templates/employees/change_title.html:21 145 | #: templates/employees/department_detail.html:42 146 | #: templates/employees/employee_detail.html:34 147 | #: templates/employees/employee_detail.html:60 148 | #: templates/employees/employee_detail.html:85 149 | msgid "Since" 150 | msgstr "이후" 151 | 152 | #: templates/employees/change_title.html:22 153 | #: templates/employees/employee_detail.html:35 154 | #: templates/employees/employee_detail.html:61 155 | msgid "To" 156 | msgstr "에" 157 | 158 | #: templates/employees/change_title.html:38 159 | #: templates/employees/department_detail.html:29 160 | #: templates/employees/department_detail.html:56 161 | #: templates/employees/employee_detail.html:23 162 | #: templates/employees/employee_detail.html:50 163 | #: templates/employees/employee_detail.html:99 164 | msgid "Change" 165 | msgstr "" 166 | 167 | #: templates/employees/department_detail.html:28 168 | #: templates/employees/employee_detail.html:22 169 | msgid "Delete" 170 | msgstr "" 171 | 172 | #: templates/employees/department_detail.html:37 173 | msgid "Department Managers" 174 | msgstr "부서 관리자" 175 | 176 | #: templates/employees/department_detail.html:41 177 | msgid "Manager" 178 | msgstr "매니저" 179 | 180 | #: templates/employees/employee_detail.html:55 templates/employees/menu.html:3 181 | msgid "Departments" 182 | msgstr "학과" 183 | 184 | #: templates/employees/employee_detail.html:59 185 | msgid "Department" 186 | msgstr "학과" 187 | 188 | #: templates/employees/employee_detail.html:80 189 | #: templates/employees/employee_detail.html:84 190 | msgid "Salary" 191 | msgstr "봉급" 192 | 193 | #: views.py:107 194 | msgid "current salary" 195 | msgstr "현재 급여" 196 | -------------------------------------------------------------------------------- /demo/employees/locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/employees/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /demo/employees/locale/zh_Hans/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/employees/locale/zh_Hans/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /demo/employees/locale/zh_Hans/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # distributed under the same license as the Django Material package. 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: django-material\n" 5 | "Report-Msgid-Bugs-To: \n" 6 | "POT-Creation-Date: 2017-05-09 09:52+0000\n" 7 | "Language: zh_CN\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "Plural-Forms: nplurals=1; plural=0;\n" 12 | 13 | #: apps.py:10 templates/employees/department_employees.html:7 14 | #: templates/employees/menu.html:4 15 | msgid "Employees" 16 | msgstr "雇员" 17 | 18 | #: forms.py:8 19 | msgid "manager" 20 | msgstr "经理" 21 | 22 | #: forms.py:26 23 | msgid "position" 24 | msgstr "位置" 25 | 26 | #: forms.py:44 models.py:76 models.py:85 27 | msgid "salary" 28 | msgstr "薪水" 29 | 30 | #: models.py:11 31 | msgid "code" 32 | msgstr "码" 33 | 34 | #: models.py:12 35 | msgid "name" 36 | msgstr "名称" 37 | 38 | #: models.py:15 models.py:27 models.py:42 39 | msgid "department" 40 | msgstr "部" 41 | 42 | #: models.py:16 43 | msgid "departments" 44 | msgstr "部门" 45 | 46 | #: models.py:26 models.py:41 models.py:65 models.py:75 models.py:91 47 | msgid "employee" 48 | msgstr "雇员" 49 | 50 | #: models.py:28 models.py:43 models.py:77 models.py:93 51 | msgid "from" 52 | msgstr "自" 53 | 54 | #: models.py:29 models.py:44 models.py:78 models.py:94 55 | msgid "to" 56 | msgstr "至" 57 | 58 | #: models.py:34 59 | msgid "department employee" 60 | msgstr "部门员工" 61 | 62 | #: models.py:35 63 | msgid "department employees" 64 | msgstr "部门员工" 65 | 66 | #: models.py:49 67 | msgid "department manager" 68 | msgstr "部门经理" 69 | 70 | #: models.py:50 71 | msgid "department managers" 72 | msgstr "部门经理" 73 | 74 | #: models.py:57 75 | msgid "employee number" 76 | msgstr "员工编号" 77 | 78 | #: models.py:58 79 | msgid "birthday" 80 | msgstr "生日" 81 | 82 | #: models.py:59 83 | msgid "first name" 84 | msgstr "名字" 85 | 86 | #: models.py:60 87 | msgid "last name" 88 | msgstr "姓" 89 | 90 | #: models.py:61 91 | msgid "gender" 92 | msgstr "性别" 93 | 94 | #: models.py:62 95 | msgid "hire date" 96 | msgstr "聘用日期" 97 | 98 | #: models.py:66 views.py:137 99 | msgid "employees" 100 | msgstr "雇员" 101 | 102 | #: models.py:86 103 | msgid "salaries" 104 | msgstr "工资" 105 | 106 | #: models.py:92 models.py:99 107 | msgid "title" 108 | msgstr "标题" 109 | 110 | #: models.py:100 111 | msgid "titles" 112 | msgstr "标题" 113 | 114 | #: templates/employees/change_manager.html:7 115 | msgid "Change Manager" 116 | msgstr "更改" 117 | 118 | #: templates/employees/change_salary.html:7 119 | msgid "Change Salary" 120 | msgstr "换薪水" 121 | 122 | #: templates/employees/change_salary.html:14 views.py:39 123 | msgid "Salary History" 124 | msgstr "工资历史" 125 | 126 | #: templates/employees/change_salary.html:43 127 | msgid "Set new Salary" 128 | msgstr "新工资" 129 | 130 | #: templates/employees/change_title.html:7 131 | msgid "Change Title" 132 | msgstr "更改标题" 133 | 134 | #: templates/employees/change_title.html:16 135 | #: templates/employees/employee_detail.html:29 136 | msgid "Positions" 137 | msgstr "位置" 138 | 139 | #: templates/employees/change_title.html:20 140 | #: templates/employees/employee_detail.html:33 141 | msgid "Position" 142 | msgstr "位置" 143 | 144 | #: templates/employees/change_title.html:21 145 | #: templates/employees/department_detail.html:42 146 | #: templates/employees/employee_detail.html:34 147 | #: templates/employees/employee_detail.html:60 148 | #: templates/employees/employee_detail.html:85 149 | msgid "Since" 150 | msgstr "自" 151 | 152 | #: templates/employees/change_title.html:22 153 | #: templates/employees/employee_detail.html:35 154 | #: templates/employees/employee_detail.html:61 155 | msgid "To" 156 | msgstr "至" 157 | 158 | #: templates/employees/change_title.html:38 159 | #: templates/employees/department_detail.html:29 160 | #: templates/employees/department_detail.html:56 161 | #: templates/employees/employee_detail.html:23 162 | #: templates/employees/employee_detail.html:50 163 | #: templates/employees/employee_detail.html:99 164 | msgid "Change" 165 | msgstr "" 166 | 167 | #: templates/employees/department_detail.html:28 168 | #: templates/employees/employee_detail.html:22 169 | msgid "Delete" 170 | msgstr "" 171 | 172 | #: templates/employees/department_detail.html:37 173 | msgid "Department Managers" 174 | msgstr "部门经理" 175 | 176 | #: templates/employees/department_detail.html:41 177 | msgid "Manager" 178 | msgstr "经理" 179 | 180 | #: templates/employees/employee_detail.html:55 templates/employees/menu.html:3 181 | msgid "Departments" 182 | msgstr "部门" 183 | 184 | #: templates/employees/employee_detail.html:59 185 | msgid "Department" 186 | msgstr "部" 187 | 188 | #: templates/employees/employee_detail.html:80 189 | #: templates/employees/employee_detail.html:84 190 | msgid "Salary" 191 | msgstr "薪水" 192 | 193 | #: views.py:107 194 | msgid "current salary" 195 | msgstr "当前工资" 196 | -------------------------------------------------------------------------------- /demo/employees/managers.py: -------------------------------------------------------------------------------- 1 | from datetime import date 2 | 3 | from django.db import models 4 | from django.utils import timezone 5 | 6 | 7 | class TemporalQuerySet(models.QuerySet): 8 | """Temporal manager for models with `from_date`/`to_date` fields.""" 9 | 10 | def set(self, **kwargs): 11 | today = timezone.now().date() 12 | 13 | self.filter( 14 | from_date__lte=today, 15 | to_date__gt=today, 16 | ).update(to_date=today) 17 | 18 | self.filter( 19 | from_date=today, 20 | to_date=today, 21 | ).delete() 22 | 23 | return self.create( 24 | from_date=today, 25 | to_date=date(9999, 1, 1), 26 | **kwargs 27 | ) 28 | 29 | def current(self): 30 | today = timezone.now().date() 31 | 32 | return self.filter( 33 | from_date__lte=today, 34 | to_date__gt=today, 35 | ).first() 36 | -------------------------------------------------------------------------------- /demo/employees/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.2 on 2016-12-20 08:19 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Department', 19 | fields=[ 20 | ('dept_no', models.CharField(max_length=4, primary_key=True, serialize=False)), 21 | ('dept_name', models.CharField(max_length=40, unique=True)), 22 | ], 23 | options={ 24 | 'ordering': ['dept_no'], 25 | 'db_table': 'departments', 26 | }, 27 | ), 28 | migrations.CreateModel( 29 | name='DeptEmp', 30 | fields=[ 31 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 32 | ('from_date', models.DateField()), 33 | ('to_date', models.DateField()), 34 | ('department', models.ForeignKey(db_column='dept_no', on_delete=django.db.models.deletion.DO_NOTHING, to='employees.Department')), 35 | ], 36 | options={ 37 | 'db_table': 'dept_emp', 38 | }, 39 | ), 40 | migrations.CreateModel( 41 | name='DeptManager', 42 | fields=[ 43 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 44 | ('from_date', models.DateField()), 45 | ('to_date', models.DateField()), 46 | ('department', models.ForeignKey(db_column='dept_no', on_delete=django.db.models.deletion.DO_NOTHING, to='employees.Department')), 47 | ], 48 | options={ 49 | 'ordering': ['-from_date'], 50 | 'db_table': 'dept_manager', 51 | }, 52 | ), 53 | migrations.CreateModel( 54 | name='Employee', 55 | fields=[ 56 | ('emp_no', models.IntegerField(primary_key=True, serialize=False)), 57 | ('birth_date', models.DateField()), 58 | ('first_name', models.CharField(max_length=14)), 59 | ('last_name', models.CharField(max_length=16)), 60 | ('gender', models.CharField(max_length=1)), 61 | ('hire_date', models.DateField()), 62 | ], 63 | options={ 64 | 'db_table': 'employees', 65 | }, 66 | ), 67 | migrations.CreateModel( 68 | name='Salary', 69 | fields=[ 70 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 71 | ('salary', models.IntegerField()), 72 | ('from_date', models.DateField()), 73 | ('to_date', models.DateField()), 74 | ('employee', models.ForeignKey(db_column='emp_no', on_delete=django.db.models.deletion.DO_NOTHING, to='employees.Employee')), 75 | ], 76 | options={ 77 | 'verbose_name': 'Salary', 78 | 'verbose_name_plural': 'Salaries', 79 | 'ordering': ['-from_date'], 80 | 'db_table': 'salaries', 81 | }, 82 | ), 83 | migrations.CreateModel( 84 | name='Title', 85 | fields=[ 86 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 87 | ('title', models.CharField(max_length=50)), 88 | ('from_date', models.DateField()), 89 | ('to_date', models.DateField(blank=True, null=True)), 90 | ('employee', models.ForeignKey(db_column='emp_no', on_delete=django.db.models.deletion.DO_NOTHING, to='employees.Employee')), 91 | ], 92 | options={ 93 | 'db_table': 'titles', 94 | }, 95 | ), 96 | migrations.AddField( 97 | model_name='deptmanager', 98 | name='employee', 99 | field=models.ForeignKey(db_column='emp_no', on_delete=django.db.models.deletion.DO_NOTHING, to='employees.Employee'), 100 | ), 101 | migrations.AddField( 102 | model_name='deptemp', 103 | name='employee', 104 | field=models.ForeignKey(db_column='emp_no', on_delete=django.db.models.deletion.DO_NOTHING, to='employees.Employee'), 105 | ), 106 | ] 107 | -------------------------------------------------------------------------------- /demo/employees/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/employees/migrations/__init__.py -------------------------------------------------------------------------------- /demo/employees/models.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | from django.db import models 4 | from django.utils.encoding import python_2_unicode_compatible 5 | from django.utils.translation import ugettext_lazy as _ 6 | from .managers import TemporalQuerySet 7 | 8 | 9 | @python_2_unicode_compatible 10 | class Department(models.Model): 11 | dept_no = models.CharField(_('code'), primary_key=True, max_length=4) 12 | dept_name = models.CharField(_('name'), unique=True, max_length=40) 13 | 14 | class Meta: 15 | verbose_name = _('department') 16 | verbose_name_plural = _('departments') 17 | db_table = 'departments' 18 | ordering = ['dept_no'] 19 | 20 | def __str__(self): 21 | return self.dept_name 22 | 23 | 24 | @python_2_unicode_compatible 25 | class DeptEmp(models.Model): 26 | employee = models.ForeignKey('Employee', on_delete=models.CASCADE, db_column='emp_no', verbose_name=_('employee')) 27 | department = models.ForeignKey(Department, on_delete=models.CASCADE, db_column='dept_no', verbose_name=_('department')) 28 | from_date = models.DateField(_('from')) 29 | to_date = models.DateField(_('to')) 30 | 31 | objects = TemporalQuerySet.as_manager() 32 | 33 | class Meta: 34 | verbose_name = _('department employee') 35 | verbose_name_plural = _('department employees') 36 | db_table = 'dept_emp' 37 | 38 | def __str__(self): 39 | return "{} - {}".format(self.employee, self.department) 40 | 41 | 42 | @python_2_unicode_compatible 43 | class DeptManager(models.Model): 44 | employee = models.ForeignKey('Employee', on_delete=models.CASCADE, db_column='emp_no', verbose_name=_('employee')) 45 | department = models.ForeignKey(Department, on_delete=models.CASCADE, db_column='dept_no', verbose_name=_('department')) 46 | from_date = models.DateField(_('from')) 47 | to_date = models.DateField(_('to')) 48 | 49 | objects = TemporalQuerySet.as_manager() 50 | 51 | class Meta: 52 | verbose_name = _('department manager') 53 | verbose_name_plural = _('department managers') 54 | db_table = 'dept_manager' 55 | ordering = ['-from_date'] 56 | 57 | def __str__(self): 58 | return "{} - {}".format(self.employee, self.department) 59 | 60 | 61 | @python_2_unicode_compatible 62 | class Employee(models.Model): 63 | emp_no = models.IntegerField(_('employee number'), primary_key=True) 64 | birth_date = models.DateField(_('birthday')) 65 | first_name = models.CharField(_('first name'), max_length=14) 66 | last_name = models.CharField(_('last name'), max_length=16) 67 | gender = models.CharField(_('gender'), max_length=1) 68 | hire_date = models.DateField(_('hire date')) 69 | 70 | class Meta: 71 | verbose_name = _('employee') 72 | verbose_name_plural = _('employees') 73 | db_table = 'employees' 74 | 75 | def __str__(self): 76 | return "{} {}".format(self.first_name, self.last_name) 77 | 78 | 79 | @python_2_unicode_compatible 80 | class Salary(models.Model): 81 | employee = models.ForeignKey(Employee, on_delete=models.CASCADE, db_column='emp_no', verbose_name=_('employee')) 82 | salary = models.IntegerField(_('salary')) 83 | from_date = models.DateField(_('from')) 84 | to_date = models.DateField(_('to')) 85 | 86 | objects = TemporalQuerySet.as_manager() 87 | 88 | class Meta: 89 | db_table = 'salaries' 90 | ordering = ['-from_date'] 91 | verbose_name = _('salary') 92 | verbose_name_plural = _('salaries') 93 | 94 | def __str__(self): 95 | return "{} - {}".format(self.employee, self.salary) 96 | 97 | 98 | @python_2_unicode_compatible 99 | class Title(models.Model): 100 | employee = models.ForeignKey(Employee, on_delete=models.CASCADE, db_column='emp_no', verbose_name=_('employee')) 101 | title = models.CharField(_('title'), max_length=50) 102 | from_date = models.DateField(_('from')) 103 | to_date = models.DateField(_('to'), blank=True, null=True) 104 | 105 | objects = TemporalQuerySet.as_manager() 106 | 107 | class Meta: 108 | verbose_name = _('title') 109 | verbose_name_plural = _('titles') 110 | db_table = 'titles' 111 | 112 | def __str__(self): 113 | return "{} - {}".format(self.employee, self.title) 114 | -------------------------------------------------------------------------------- /demo/employees/templates/employees/base_module.html: -------------------------------------------------------------------------------- 1 | {% extends 'material/frontend/base_module.html' %} 2 | 3 | {% block js %} 4 | {{ block.super }} 5 | 6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /demo/employees/templates/employees/change_manager.html: -------------------------------------------------------------------------------- 1 | {% extends 'employees/base_module.html' %} 2 | {% load i18n material_frontend %} 3 | 4 | {% block breadcrumbs_items %} 5 | {{ department|verbose_name_plural|title }} 6 | {{ department }} 7 | {% trans 'Change Manager' %} 8 | {% endblock %} 9 | 10 | {% block content %} 11 |
12 |
13 | {{ form }} 14 |
15 |
16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /demo/employees/templates/employees/change_salary.html: -------------------------------------------------------------------------------- 1 | {% extends 'employees/base_module.html' %} 2 | {% load i18n material_frontend %} 3 | 4 | {% block breadcrumbs_items %} 5 | {{ employee|verbose_name_plural|title }} 6 | {{ employee }} 7 | {% trans 'Change Salary' %} 8 | {% endblock %} 9 | 10 | {% block content %} 11 |
12 |
13 |
14 |
{% trans 'Salary History' %}
15 | 16 | 17 | 30 |
31 |
32 |
33 |
34 |
35 |
36 | {% csrf_token %} 37 |
38 | {% form %} 39 | {% part form.salary prefix %}attach_money{% endpart %} 40 | {% endform %} 41 |
42 |
43 | 44 |
45 |
46 |
47 |
48 | {% endblock %} 49 | -------------------------------------------------------------------------------- /demo/employees/templates/employees/change_title.html: -------------------------------------------------------------------------------- 1 | {% extends 'employees/base_module.html' %} 2 | {% load i18n material_frontend %} 3 | 4 | {% block breadcrumbs_items %} 5 | {{ employee|verbose_name_plural|title }} 6 | {{ employee }} 7 | {% trans 'Change Title' %} 8 | {% endblock %} 9 | 10 | {% block content %} 11 |
12 |
13 |
14 | {% csrf_token %} 15 |
16 |
{% trans 'Positions' %}
17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | {% for title in employee.title_set.all %} 27 | 28 | 29 | 30 | 31 | 32 | {% endfor %} 33 | 34 |
{% trans 'Position' %}{% trans 'Since' %}{% trans 'To' %}
{{ title.title }}{{ title.from_date }}{{ title.to_date }}
35 | {{ form }} 36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 | {% endblock %} 44 | -------------------------------------------------------------------------------- /demo/employees/templates/employees/department_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'material/frontend/views/detail.html' %} 2 | {% load i18n material_frontend %} 3 | 4 | {% block content %} 5 |
6 |
7 |
8 | {% block card %} 9 |
{{ view.model|verbose_name|title }}: {{ object }}
10 | 11 | {% for field_name, value in object_data %} 12 | 13 | 14 | 15 | {% endfor %} 16 | 17 | {% endblock %} 18 | 19 | 20 | 23 | 24 |
{{ field_name }}{{ value }}
Employees 21 | {{ object.deptemp_set.count }} 22 |
25 |
26 |
27 |
28 | {% if delete_url %}{% trans 'Delete' %}{% endif %} 29 | {% if change_url %}{% trans 'Change' %}{% endif %} 30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
{% trans 'Department Managers' %}
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | {% for manager in object.deptmanager_set.all %} 47 | 48 | 49 | 50 | 51 | {% endfor %} 52 | 53 |
{% trans 'Manager' %}{% trans 'Since' %}
{{ manager.employee }}{{ manager.from_date }}
54 |
55 | 58 |
59 |
60 | {% endblock %} 61 | -------------------------------------------------------------------------------- /demo/employees/templates/employees/department_employees.html: -------------------------------------------------------------------------------- 1 | {% extends 'material/frontend/views/list.html' %} 2 | {% load i18n material_frontend %} 3 | 4 | {% block breadcrumbs_items %} 5 | {{ department|verbose_name_plural|title }} 6 | {{ department }} 7 | {% trans 'Employees' %} 8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /demo/employees/templates/employees/employee_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'material/frontend/views/detail.html' %} 2 | {% load i18n material_frontend %} 3 | 4 | {% block content %} 5 |
6 |
7 |
8 | {% block card %} 9 |
{{ view.model|verbose_name|title }}: {{ object }}
10 | 11 | {% for field_name, value in object_data %} 12 | 13 | 14 | 15 | {% endfor %} 16 | 17 | {% endblock %} 18 |
{{ field_name }}{{ value }}
19 |
20 |
21 |
22 | {% if delete_url %}{% trans 'Delete' %}{% endif %} 23 | {% if change_url %}{% trans 'Change' %}{% endif %} 24 |
25 |
26 |
27 |
28 |
29 |
{% trans 'Positions' %}
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | {% for title in object.title_set.all|slice:":10" %} 40 | 41 | 42 | 43 | 44 | 45 | {% endfor %} 46 | 47 |
{% trans 'Position' %}{% trans 'Since' %}{% trans 'To' %}
{{ title.title }}{{ title.from_date }}{{ title.to_date }}
48 |
49 | 52 |
53 |
54 |
55 |
{% trans 'Departments' %}
56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | {% for dept in object.deptemp_set.all|slice:":10" %} 66 | 67 | 68 | 69 | 70 | 71 | {% endfor %} 72 | 73 |
{% trans 'Department' %}{% trans 'Since' %}{% trans 'To' %}
{{ dept.department }}{{ dept.from_date }}{{ dept.to_date }}
74 |
75 |
76 |
77 |
78 |
79 |
80 |
{% trans 'Salary' %}
81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | {% for salary in object.salary_set.all|slice:":10" %} 90 | 91 | 92 | 93 | 94 | {% endfor %} 95 | 96 |
{% trans 'Salary' %}{% trans 'Since' %}
${{ salary.salary }}{{ salary.from_date }}
97 |
98 | 101 |
102 |
103 | {% endblock %} 104 | -------------------------------------------------------------------------------- /demo/employees/templates/employees/menu.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 6 | -------------------------------------------------------------------------------- /demo/employees/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url, include 2 | from django.views import generic 3 | 4 | from . import views 5 | 6 | 7 | urlpatterns = [ 8 | url('^$', generic.RedirectView.as_view( 9 | url='./departments/'), name="index"), 10 | url('^departments/', include(views.DepartmentViewSet().urls)), 11 | url('^employees/', include(views.EmployeeViewSet().urls)), 12 | ] 13 | -------------------------------------------------------------------------------- /demo/employees/views.py: -------------------------------------------------------------------------------- 1 | import json 2 | from django.contrib.auth.decorators import login_required 3 | from django.utils import timezone 4 | from django.shortcuts import get_object_or_404, render 5 | from django.utils.translation import ugettext, ugettext_lazy as _ 6 | 7 | from material.frontend.views import ModelViewSet, ListModelView 8 | 9 | from . import models, forms 10 | 11 | 12 | @login_required 13 | def change_manager(request, department_pk): 14 | department = get_object_or_404(models.Department, pk=department_pk) 15 | form = forms.ChangeManagerForm(department=department, data=request.POST or None) 16 | 17 | if form.is_valid(): 18 | form.save() 19 | 20 | return render(request, 'employees/change_manager.html', { 21 | 'form': form, 22 | 'department': department, 23 | 'model': models.Department 24 | }) 25 | 26 | 27 | @login_required 28 | def change_salary(request, employee_pk): 29 | employee = get_object_or_404(models.Employee, pk=employee_pk) 30 | form = forms.ChangeSalaryForm(employee=employee, data=request.POST or None) 31 | 32 | if form.is_valid(): 33 | form.save() 34 | 35 | salaries = employee.salary_set.all().order_by('from_date') 36 | salary_data = { 37 | 'labels': [salary.from_date.strftime('%Y-%m-%d') for salary in salaries], 38 | 'datasets': [ 39 | {'data': [salary.salary for salary in salaries], 'label': ugettext('Salary History')} 40 | ] 41 | } 42 | 43 | return render(request, 'employees/change_salary.html', { 44 | 'form': form, 45 | 'employee': employee, 46 | 'salary_history': json.dumps(salary_data), 47 | 'model': models.Employee 48 | }) 49 | 50 | 51 | @login_required 52 | def change_title(request, employee_pk): 53 | employee = get_object_or_404(models.Employee, pk=employee_pk) 54 | form = forms.ChangeTitleForm(employee=employee, data=request.POST or None) 55 | 56 | if form.is_valid(): 57 | form.save() 58 | 59 | return render(request, 'employees/change_title.html', { 60 | 'form': form, 61 | 'employee': employee, 62 | 'model': models.Employee 63 | }) 64 | 65 | 66 | class DepartmentEmployesListView(ListModelView): 67 | model = models.Employee 68 | list_display = ('emp_no', 'first_name', 'last_name', 'current_salary') 69 | template_name = 'employees/department_employees.html' 70 | 71 | def get_queryset(self): 72 | today = timezone.now().date() 73 | department = get_object_or_404(models.Department, pk=self.kwargs['department_pk']) 74 | queryset = super(DepartmentEmployesListView, self).get_queryset() 75 | 76 | return queryset.filter( 77 | deptemp__department=department, 78 | deptemp__from_date__lte=today, 79 | deptemp__to_date__gt=today 80 | ) 81 | 82 | def get_context_data(self, **kwargs): 83 | department = get_object_or_404(models.Department, pk=self.kwargs['department_pk']) 84 | return super(DepartmentEmployesListView, self).get_context_data( 85 | department=department, **kwargs) 86 | 87 | 88 | class EmployeeViewSet(ModelViewSet): 89 | model = models.Employee 90 | list_display = ('emp_no', 'first_name', 'last_name', 'birth_date', 'current_salary') 91 | 92 | change_salary_view = [ 93 | r'^(?P.+)/change_salary/$', 94 | change_salary, 95 | '{model_name}_change_salary' 96 | ] 97 | 98 | change_title_view = [ 99 | r'^(?P.+)/change_title/$', 100 | change_title, 101 | '{model_name}_change_title' 102 | ] 103 | 104 | def current_salary(self, obj): 105 | salary = obj.salary_set.current() 106 | return salary.salary if salary is not None else 0 107 | current_salary.short_description = _('current salary') 108 | 109 | 110 | class DepartmentViewSet(ModelViewSet): 111 | model = models.Department 112 | list_display = ('dept_no', 'dept_name', 'manager', 'employees') 113 | 114 | change_manager_view = [ 115 | r'^(?P.+)/change_manager/$', 116 | change_manager, 117 | '{model_name}_change_manager' 118 | ] 119 | 120 | employees_list_view = [ 121 | r'^(?P.+)/employees/$', 122 | DepartmentEmployesListView.as_view(viewset=EmployeeViewSet()), 123 | '{model_name}_employees' 124 | ] 125 | 126 | def manager(self, obj, today=None): 127 | if today is None: 128 | today = timezone.now().date() 129 | manager = obj.deptmanager_set.filter( 130 | from_date__lte=today, 131 | to_date__gt=today 132 | ).first() 133 | return manager.employee if manager is not None else '' 134 | 135 | def employees(self, obj): 136 | return obj.deptemp_set.count() 137 | employees.short_description = _('employees') 138 | -------------------------------------------------------------------------------- /demo/hr/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'demo.hr.apps.HrConfig' -------------------------------------------------------------------------------- /demo/hr/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | from django.contrib import admin 5 | from .models import * 6 | # Register your models here. 7 | @admin.register(department) 8 | class departmentAdmin(admin.ModelAdmin): 9 | pass 10 | 11 | @admin.register(employee) 12 | class employeeAdmin(admin.ModelAdmin): 13 | pass -------------------------------------------------------------------------------- /demo/hr/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from material.frontend.apps import ModuleMixin 3 | from material.frontend.registry import modules 4 | 5 | 6 | class HrConfig(AppConfig,ModuleMixin): 7 | name = 'demo.hr' 8 | label = 'hr' 9 | verbose_name='资源管理' 10 | icon = 'book' 11 | -------------------------------------------------------------------------------- /demo/hr/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.7 on 2017-11-24 07:47 3 | from __future__ import unicode_literals 4 | 5 | from django.conf import settings 6 | from django.db import migrations, models 7 | import django.db.models.deletion 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | initial = True 13 | 14 | dependencies = [ 15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='department', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('name', models.CharField(max_length=1024, verbose_name='Department Name')), 24 | ('note', models.CharField(max_length=1024, verbose_name='Note')), 25 | ], 26 | options={ 27 | 'verbose_name': '部门', 28 | 'verbose_name_plural': '部门', 29 | 'db_table': 'hr.department', 30 | }, 31 | ), 32 | migrations.CreateModel( 33 | name='employee', 34 | fields=[ 35 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 36 | ('birthday', models.DateTimeField(verbose_name='Date of Birth')), 37 | ('passport_id', models.CharField(max_length=1024, verbose_name='passport No')), 38 | ('sex', models.CharField(choices=[('M', 'Male'), ('F', 'Female'), ('O', 'Other')], max_length=1)), 39 | ('marital', models.CharField(choices=[('single', 'Single'), ('married', 'Married'), ('widower', 'Widower'), ('divorced', 'Divorced')], max_length=10)), 40 | ('address', models.CharField(max_length=1024, verbose_name='Working Address')), 41 | ('work_phone', models.CharField(max_length=1024, verbose_name='Work Phone')), 42 | ('mobile_phone', models.CharField(max_length=1024, verbose_name='Work Mobile')), 43 | ('work_email', models.CharField(max_length=1024, verbose_name='Work Email')), 44 | ('work_location', models.CharField(max_length=1024, verbose_name='Office Location')), 45 | ('notes', models.CharField(max_length=1024, verbose_name='Notes')), 46 | ('Manager', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='lead_member', to='hr.employee')), 47 | ('department', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='member', to='hr.department')), 48 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='账号绑定')), 49 | ], 50 | options={ 51 | 'verbose_name': '雇员', 52 | 'verbose_name_plural': '雇员', 53 | 'db_table': 'hr.employee', 54 | }, 55 | ), 56 | migrations.AddField( 57 | model_name='department', 58 | name='leader', 59 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='emp_children', to='hr.employee'), 60 | ), 61 | migrations.AddField( 62 | model_name='department', 63 | name='parent', 64 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='dep_children', to='hr.department'), 65 | ), 66 | ] 67 | -------------------------------------------------------------------------------- /demo/hr/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/hr/migrations/__init__.py -------------------------------------------------------------------------------- /demo/hr/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | 4 | # Create your models here. 5 | class employee(models.Model): 6 | 7 | birthday = models.DateTimeField(verbose_name=u"Date of Birth") 8 | passport_id = models.CharField(verbose_name=u'passport No',max_length=1024) 9 | sex = models.CharField( 10 | max_length=1, 11 | choices=( 12 | ("M", "Male"), 13 | ("F", "Female"), 14 | ("O", "Other"))) 15 | marital= models.CharField( 16 | max_length=10, 17 | choices=(('single', 'Single'), ('married', 'Married'), ('widower', 'Widower'), ('divorced', 'Divorced'))) 18 | department= models.ForeignKey('hr.department',related_name='member') 19 | address=models.CharField(verbose_name=u'Working Address',max_length=1024) 20 | work_phone= models.CharField(verbose_name=u'Work Phone',max_length=1024) 21 | mobile_phone= models.CharField(verbose_name=u'Work Mobile',max_length=1024) 22 | work_email= models.CharField(verbose_name=u'Work Email',max_length=1024) 23 | work_location= models.CharField(verbose_name=u'Office Location',max_length=1024) 24 | notes= models.CharField(verbose_name=u'Notes',max_length=1024) 25 | Manager=models.ForeignKey('hr.employee', blank=True, null=True, related_name='lead_member') 26 | 27 | # 28 | user = models.OneToOneField(User, verbose_name=u"账号绑定") 29 | # image: all image fields are base64 encoded and PIL-supported 30 | #image=models.ImageField(verbose_name=u"Photo") 31 | class Meta: 32 | db_table = 'hr.employee' 33 | verbose_name = '雇员' 34 | verbose_name_plural = "雇员" 35 | 36 | def __str__(self): 37 | return self.user.username 38 | 39 | class department(models.Model): 40 | 41 | class Meta: 42 | db_table = 'hr.department' 43 | verbose_name = '部门' 44 | verbose_name_plural = "部门" 45 | 46 | name = models.CharField(verbose_name=u'Department Name',max_length=1024) 47 | parent = models.ForeignKey('hr.department', blank=True, null=True, related_name='dep_children') 48 | leader = models.ForeignKey('hr.employee', blank=True, null=True, related_name='emp_children') 49 | note=models.CharField(verbose_name=u'Note',max_length=1024) 50 | 51 | 52 | def __str__(self): 53 | # dep_tree = _get_dep_tree(self) 54 | return self.name 55 | # 56 | # def _get_dep_tree(self): 57 | # ret ='' 58 | # if (self.parent is not None): 59 | # ret = _get_dep_tree(self.parent) 60 | # 61 | # return ret + '/'+dep.name 62 | 63 | -------------------------------------------------------------------------------- /demo/hr/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /demo/hr/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url, include 2 | from django.views import generic 3 | from . import views 4 | 5 | urlpatterns = [ 6 | url('^$', generic.RedirectView.as_view(url='./employee/', permanent=False), name="index"), 7 | url('^employee/', include(views.EmployeeViewSet().urls)), 8 | url('^department/', include(views.DepartmentViewSet().urls)), 9 | ] -------------------------------------------------------------------------------- /demo/hr/views.py: -------------------------------------------------------------------------------- 1 | from material.frontend.views import ModelViewSet 2 | from . import models 3 | 4 | class EmployeeViewSet(ModelViewSet): 5 | 6 | model = models.employee 7 | 8 | class DepartmentViewSet(ModelViewSet): 9 | 10 | model = models.department -------------------------------------------------------------------------------- /demo/integration/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'demo.integration.apps.IntegrationAppConfig' 2 | -------------------------------------------------------------------------------- /demo/integration/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from material.frontend.apps import ModuleMixin 3 | from django.utils.translation import ugettext_lazy as _ 4 | 5 | 6 | class IntegrationAppConfig(ModuleMixin, AppConfig): 7 | name = 'demo.integration' 8 | icon = 'extension' 9 | verbose_name = _("CRUD sample") 10 | 11 | def has_perm(self, user): 12 | return user.is_superuser 13 | -------------------------------------------------------------------------------- /demo/integration/locale/de/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/integration/locale/de/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /demo/integration/locale/de/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # distributed under the same license as the Django Material package. 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: django-material\n" 5 | "Report-Msgid-Bugs-To: \n" 6 | "POT-Creation-Date: 2017-05-09 09:52+0000\n" 7 | "Language: de\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 12 | 13 | #: admin.py:45 14 | msgid "wiki" 15 | msgstr "" 16 | 17 | #: admin.py:83 18 | msgid "map" 19 | msgstr "karte" 20 | 21 | #: admin.py:87 22 | msgid "short description" 23 | msgstr "kurze beschreibung" 24 | 25 | #: admin.py:109 views.py:69 26 | msgid "sea area" 27 | msgstr "seegebiet" 28 | 29 | #: admin.py:120 views.py:21 30 | msgid "Details" 31 | msgstr "" 32 | 33 | #: admin.py:123 views.py:25 34 | msgid "Fun facts" 35 | msgstr "Fakten" 36 | 37 | #: admin.py:138 38 | msgid "surrounded oceans" 39 | msgstr "umgeben von ozeanen" 40 | 41 | #: admin.py:142 42 | msgid "countries count" 43 | msgstr "länder zählen" 44 | 45 | #: admin.py:182 admin.py:204 views.py:49 46 | msgid "became independent in XX century" 47 | msgstr "Wurde im XX Jahrhundert unabhängig" 48 | 49 | #: apps.py:9 50 | msgid "CRUD sample" 51 | msgstr "CRUD Probe" 52 | 53 | #: models.py:9 models.py:26 models.py:48 models.py:82 models.py:99 54 | msgid "name" 55 | msgstr "" 56 | 57 | #: models.py:10 models.py:30 models.py:49 58 | msgid "area" 59 | msgstr "bereich" 60 | 61 | #: models.py:11 62 | msgid "slug" 63 | msgstr "" 64 | 65 | #: models.py:12 66 | msgid "description" 67 | msgstr "beschreibung" 68 | 69 | #: models.py:13 70 | msgid "map url" 71 | msgstr "karte url" 72 | 73 | #: models.py:16 models.py:28 74 | msgid "ocean" 75 | msgstr "ozean" 76 | 77 | #: models.py:17 models.py:60 78 | msgid "oceans" 79 | msgstr "ozeane" 80 | 81 | #: models.py:27 82 | msgid "parent" 83 | msgstr "elternteil" 84 | 85 | #: models.py:30 86 | msgid "km²" 87 | msgstr "" 88 | 89 | #: models.py:31 90 | msgid "average depth" 91 | msgstr "durchschnittliche tiefe" 92 | 93 | #: models.py:31 models.py:32 94 | msgid "meters" 95 | msgstr "meter" 96 | 97 | #: models.py:32 98 | msgid "maximum depth" 99 | msgstr "maximale tiefe" 100 | 101 | #: models.py:38 102 | msgid "sea" 103 | msgstr "meer" 104 | 105 | #: models.py:39 106 | msgid "seas" 107 | msgstr "meere" 108 | 109 | #: models.py:50 models.py:101 110 | msgid "population" 111 | msgstr "bevölkerung" 112 | 113 | #: models.py:51 114 | msgid "population density" 115 | msgstr "bevölkerungsdichte" 116 | 117 | #: models.py:54 118 | msgid "largest country" 119 | msgstr "größtes land" 120 | 121 | #: models.py:56 122 | msgid "biggest city" 123 | msgstr "größte stadt" 124 | 125 | #: models.py:57 126 | msgid "longest river" 127 | msgstr "längster fluss" 128 | 129 | #: models.py:58 130 | msgid "biggest mountain" 131 | msgstr "größter berg" 132 | 133 | #: models.py:71 models.py:86 134 | msgid "continent" 135 | msgstr "kontinent" 136 | 137 | #: models.py:72 138 | msgid "continents" 139 | msgstr "kontinente" 140 | 141 | #: models.py:81 142 | msgid "code" 143 | msgstr "" 144 | 145 | #: models.py:83 146 | msgid "independence day" 147 | msgstr "tag der unabhängigkeit" 148 | 149 | #: models.py:84 150 | msgid "gay friendly" 151 | msgstr "schwulenfreundlich" 152 | 153 | #: models.py:89 models.py:103 154 | msgid "country" 155 | msgstr "land" 156 | 157 | #: models.py:90 158 | msgid "countries" 159 | msgstr "länder" 160 | 161 | #: models.py:100 162 | msgid "is capital city" 163 | msgstr "Ist Hauptstadt" 164 | 165 | #: models.py:106 166 | msgid "city" 167 | msgstr "stadt" 168 | 169 | #: models.py:107 170 | msgid "cities" 171 | msgstr "städte" 172 | 173 | #: templates/integration/menu.html:3 174 | msgid "Cities" 175 | msgstr "Städte" 176 | 177 | #: templates/integration/menu.html:4 178 | msgid "Continents" 179 | msgstr "Kontinente" 180 | 181 | #: templates/integration/menu.html:5 182 | msgid "Countries" 183 | msgstr "Länder" 184 | 185 | #: templates/integration/menu.html:6 186 | msgid "Oceans" 187 | msgstr "Ozeane" 188 | 189 | #: templates/integration/menu.html:7 190 | msgid "Seas" 191 | msgstr "Meere" 192 | -------------------------------------------------------------------------------- /demo/integration/locale/es/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/integration/locale/es/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /demo/integration/locale/es/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # distributed under the same license as the Django Material package. 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: django-material\n" 5 | "Report-Msgid-Bugs-To: \n" 6 | "POT-Creation-Date: 2017-05-09 09:52+0000\n" 7 | "Language: es\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 12 | 13 | #: admin.py:45 14 | msgid "wiki" 15 | msgstr "" 16 | 17 | #: admin.py:83 18 | msgid "map" 19 | msgstr "mapa" 20 | 21 | #: admin.py:87 22 | msgid "short description" 23 | msgstr "breve descripción" 24 | 25 | #: admin.py:109 views.py:69 26 | msgid "sea area" 27 | msgstr "ver área" 28 | 29 | #: admin.py:120 views.py:21 30 | msgid "Details" 31 | msgstr "Detalles" 32 | 33 | #: admin.py:123 views.py:25 34 | msgid "Fun facts" 35 | msgstr "Hechos graciosos" 36 | 37 | #: admin.py:138 38 | msgid "surrounded oceans" 39 | msgstr "océanos rodeados" 40 | 41 | #: admin.py:142 42 | msgid "countries count" 43 | msgstr "los países cuentan" 44 | 45 | #: admin.py:182 admin.py:204 views.py:49 46 | msgid "became independent in XX century" 47 | msgstr "Se hizo independiente en el siglo XX" 48 | 49 | #: apps.py:9 50 | msgid "CRUD sample" 51 | msgstr "Muestra CRUD" 52 | 53 | #: models.py:9 models.py:26 models.py:48 models.py:82 models.py:99 54 | msgid "name" 55 | msgstr "nombre" 56 | 57 | #: models.py:10 models.py:30 models.py:49 58 | msgid "area" 59 | msgstr "zona" 60 | 61 | #: models.py:11 62 | msgid "slug" 63 | msgstr "" 64 | 65 | #: models.py:12 66 | msgid "description" 67 | msgstr "descripción" 68 | 69 | #: models.py:13 70 | msgid "map url" 71 | msgstr "URL del mapa" 72 | 73 | #: models.py:16 models.py:28 74 | msgid "ocean" 75 | msgstr "oceano" 76 | 77 | #: models.py:17 models.py:60 78 | msgid "oceans" 79 | msgstr "océanos" 80 | 81 | #: models.py:27 82 | msgid "parent" 83 | msgstr "cause" 84 | 85 | #: models.py:30 86 | msgid "km²" 87 | msgstr "" 88 | 89 | #: models.py:31 90 | msgid "average depth" 91 | msgstr "profundidad promedio" 92 | 93 | #: models.py:31 models.py:32 94 | msgid "meters" 95 | msgstr "metros" 96 | 97 | #: models.py:32 98 | msgid "maximum depth" 99 | msgstr "profundidad máxima" 100 | 101 | #: models.py:38 102 | msgid "sea" 103 | msgstr "mar" 104 | 105 | #: models.py:39 106 | msgid "seas" 107 | msgstr "mares" 108 | 109 | #: models.py:50 models.py:101 110 | msgid "population" 111 | msgstr "población" 112 | 113 | #: models.py:51 114 | msgid "population density" 115 | msgstr "densidad de población" 116 | 117 | #: models.py:54 118 | msgid "largest country" 119 | msgstr "el país más grande" 120 | 121 | #: models.py:56 122 | msgid "biggest city" 123 | msgstr "la ciudad más grande" 124 | 125 | #: models.py:57 126 | msgid "longest river" 127 | msgstr "rio más largo" 128 | 129 | #: models.py:58 130 | msgid "biggest mountain" 131 | msgstr "montaña mas grande" 132 | 133 | #: models.py:71 models.py:86 134 | msgid "continent" 135 | msgstr "continente" 136 | 137 | #: models.py:72 138 | msgid "continents" 139 | msgstr "continentes" 140 | 141 | #: models.py:81 142 | msgid "code" 143 | msgstr "código" 144 | 145 | #: models.py:83 146 | msgid "independence day" 147 | msgstr "día de la Independencia" 148 | 149 | #: models.py:84 150 | msgid "gay friendly" 151 | msgstr "amistoso" 152 | 153 | #: models.py:89 models.py:103 154 | msgid "country" 155 | msgstr "país" 156 | 157 | #: models.py:90 158 | msgid "countries" 159 | msgstr "países" 160 | 161 | #: models.py:100 162 | msgid "is capital city" 163 | msgstr "Es la ciudad capital" 164 | 165 | #: models.py:106 166 | msgid "city" 167 | msgstr "ciudad" 168 | 169 | #: models.py:107 170 | msgid "cities" 171 | msgstr "ciudades" 172 | 173 | #: templates/integration/menu.html:3 174 | msgid "Cities" 175 | msgstr "Ciudades" 176 | 177 | #: templates/integration/menu.html:4 178 | msgid "Continents" 179 | msgstr "Continentes" 180 | 181 | #: templates/integration/menu.html:5 182 | msgid "Countries" 183 | msgstr "Países" 184 | 185 | #: templates/integration/menu.html:6 186 | msgid "Oceans" 187 | msgstr "Océanos" 188 | 189 | #: templates/integration/menu.html:7 190 | msgid "Seas" 191 | msgstr "Mares" 192 | -------------------------------------------------------------------------------- /demo/integration/locale/fr/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/integration/locale/fr/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /demo/integration/locale/fr/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # distributed under the same license as the Django Material package. 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: django-material\n" 5 | "Report-Msgid-Bugs-To: \n" 6 | "POT-Creation-Date: 2017-05-09 09:52+0000\n" 7 | "Language: fr\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 12 | 13 | #: admin.py:45 14 | msgid "wiki" 15 | msgstr "" 16 | 17 | #: admin.py:83 18 | msgid "map" 19 | msgstr "carte" 20 | 21 | #: admin.py:87 22 | msgid "short description" 23 | msgstr "brève description" 24 | 25 | #: admin.py:109 views.py:69 26 | msgid "sea area" 27 | msgstr "zone maritime" 28 | 29 | #: admin.py:120 views.py:21 30 | msgid "Details" 31 | msgstr "Détails" 32 | 33 | #: admin.py:123 views.py:25 34 | msgid "Fun facts" 35 | msgstr "Faits amusants" 36 | 37 | #: admin.py:138 38 | msgid "surrounded oceans" 39 | msgstr "océans entourés" 40 | 41 | #: admin.py:142 42 | msgid "countries count" 43 | msgstr "pays comptent" 44 | 45 | #: admin.py:182 admin.py:204 views.py:49 46 | msgid "became independent in XX century" 47 | msgstr "est devenu indépendant au XXème siècle" 48 | 49 | #: apps.py:9 50 | msgid "CRUD sample" 51 | msgstr "Échantillon CRUD" 52 | 53 | #: models.py:9 models.py:26 models.py:48 models.py:82 models.py:99 54 | msgid "name" 55 | msgstr "prénom" 56 | 57 | #: models.py:10 models.py:30 models.py:49 58 | msgid "area" 59 | msgstr "région" 60 | 61 | #: models.py:11 62 | msgid "slug" 63 | msgstr "" 64 | 65 | #: models.py:12 66 | msgid "description" 67 | msgstr "la description" 68 | 69 | #: models.py:13 70 | msgid "map url" 71 | msgstr "URL de la carte" 72 | 73 | #: models.py:16 models.py:28 74 | msgid "ocean" 75 | msgstr "océan" 76 | 77 | #: models.py:17 models.py:60 78 | msgid "oceans" 79 | msgstr "océans" 80 | 81 | #: models.py:27 82 | msgid "parent" 83 | msgstr "origine" 84 | 85 | #: models.py:30 86 | msgid "km²" 87 | msgstr "" 88 | 89 | #: models.py:31 90 | msgid "average depth" 91 | msgstr "profondeur moyenne" 92 | 93 | #: models.py:31 models.py:32 94 | msgid "meters" 95 | msgstr "metros" 96 | 97 | #: models.py:32 98 | msgid "maximum depth" 99 | msgstr "profondeur maximale" 100 | 101 | #: models.py:38 102 | msgid "sea" 103 | msgstr "mer" 104 | 105 | #: models.py:39 106 | msgid "seas" 107 | msgstr "Les mers" 108 | 109 | #: models.py:50 models.py:101 110 | msgid "population" 111 | msgstr "population" 112 | 113 | #: models.py:51 114 | msgid "population density" 115 | msgstr "densité de population" 116 | 117 | #: models.py:54 118 | msgid "largest country" 119 | msgstr "le plus grand pays" 120 | 121 | #: models.py:56 122 | msgid "biggest city" 123 | msgstr "la plus grande ville" 124 | 125 | #: models.py:57 126 | msgid "longest river" 127 | msgstr "la plus longue rivière" 128 | 129 | #: models.py:58 130 | msgid "biggest mountain" 131 | msgstr "la plus grande montagne" 132 | 133 | #: models.py:71 models.py:86 134 | msgid "continent" 135 | msgstr "continent" 136 | 137 | #: models.py:72 138 | msgid "continents" 139 | msgstr "continents" 140 | 141 | #: models.py:81 142 | msgid "code" 143 | msgstr "code" 144 | 145 | #: models.py:83 146 | msgid "independence day" 147 | msgstr "le jour de l'indépendance" 148 | 149 | #: models.py:84 150 | msgid "gay friendly" 151 | msgstr "amical" 152 | 153 | #: models.py:89 models.py:103 154 | msgid "country" 155 | msgstr "pays" 156 | 157 | #: models.py:90 158 | msgid "countries" 159 | msgstr "des pays" 160 | 161 | #: models.py:100 162 | msgid "is capital city" 163 | msgstr "Est la capitale" 164 | 165 | #: models.py:106 166 | msgid "city" 167 | msgstr "ville" 168 | 169 | #: models.py:107 170 | msgid "cities" 171 | msgstr "villes" 172 | 173 | #: templates/integration/menu.html:3 174 | msgid "Cities" 175 | msgstr "Villes" 176 | 177 | #: templates/integration/menu.html:4 178 | msgid "Continents" 179 | msgstr "Continents" 180 | 181 | #: templates/integration/menu.html:5 182 | msgid "Countries" 183 | msgstr "Des pays" 184 | 185 | #: templates/integration/menu.html:6 186 | msgid "Oceans" 187 | msgstr "Océans" 188 | 189 | #: templates/integration/menu.html:7 190 | msgid "Seas" 191 | msgstr "Les mers" 192 | -------------------------------------------------------------------------------- /demo/integration/locale/ko/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/integration/locale/ko/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /demo/integration/locale/ko/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # distributed under the same license as the Django Material package. 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: django-material\n" 5 | "Report-Msgid-Bugs-To: \n" 6 | "POT-Creation-Date: 2017-05-09 09:52+0000\n" 7 | "Language: ko\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "Plural-Forms: nplurals=1; plural=0;\n" 12 | 13 | #: admin.py:45 14 | msgid "wiki" 15 | msgstr "위키" 16 | 17 | #: admin.py:83 18 | msgid "map" 19 | msgstr "지도" 20 | 21 | #: admin.py:87 22 | msgid "short description" 23 | msgstr "간단한 설명" 24 | 25 | #: admin.py:109 views.py:69 26 | msgid "sea area" 27 | msgstr "해역" 28 | 29 | #: admin.py:120 views.py:21 30 | msgid "Details" 31 | msgstr "세부" 32 | 33 | #: admin.py:123 views.py:25 34 | msgid "Fun facts" 35 | msgstr "재미있는 사실" 36 | 37 | #: admin.py:138 38 | msgid "surrounded oceans" 39 | msgstr "둘러싸인 바다" 40 | 41 | #: admin.py:142 42 | msgid "countries count" 43 | msgstr "국가 수" 44 | 45 | #: admin.py:182 admin.py:204 views.py:49 46 | msgid "became independent in XX century" 47 | msgstr "20 세기에 독립했다." 48 | 49 | #: apps.py:9 50 | msgid "CRUD sample" 51 | msgstr "CRUD 샘플" 52 | 53 | #: models.py:9 models.py:26 models.py:48 models.py:82 models.py:99 54 | msgid "name" 55 | msgstr "이름" 56 | 57 | #: models.py:10 models.py:30 models.py:49 58 | msgid "area" 59 | msgstr "지역" 60 | 61 | #: models.py:11 62 | msgid "slug" 63 | msgstr "URL 슬러그" 64 | 65 | #: models.py:12 66 | msgid "description" 67 | msgstr "기술" 68 | 69 | #: models.py:13 70 | msgid "map url" 71 | msgstr "지도 URL" 72 | 73 | #: models.py:16 models.py:28 74 | msgid "ocean" 75 | msgstr "대양" 76 | 77 | #: models.py:17 models.py:60 78 | msgid "oceans" 79 | msgstr "대양" 80 | 81 | #: models.py:27 82 | msgid "parent" 83 | msgstr "유래" 84 | 85 | #: models.py:30 86 | msgid "km²" 87 | msgstr "" 88 | 89 | #: models.py:31 90 | msgid "average depth" 91 | msgstr "평균 깊이" 92 | 93 | #: models.py:31 models.py:32 94 | msgid "meters" 95 | msgstr "미터" 96 | 97 | #: models.py:32 98 | msgid "maximum depth" 99 | msgstr "최대 깊이" 100 | 101 | #: models.py:38 102 | msgid "sea" 103 | msgstr "바다" 104 | 105 | #: models.py:39 106 | msgid "seas" 107 | msgstr "바다" 108 | 109 | #: models.py:50 models.py:101 110 | msgid "population" 111 | msgstr "인구" 112 | 113 | #: models.py:51 114 | msgid "population density" 115 | msgstr "인구 밀도" 116 | 117 | #: models.py:54 118 | msgid "largest country" 119 | msgstr "가장 큰 나라" 120 | 121 | #: models.py:56 122 | msgid "biggest city" 123 | msgstr "가장 큰 도시" 124 | 125 | #: models.py:57 126 | msgid "longest river" 127 | msgstr "가장 긴 강" 128 | 129 | #: models.py:58 130 | msgid "biggest mountain" 131 | msgstr "가장 큰 산" 132 | 133 | #: models.py:71 models.py:86 134 | msgid "continent" 135 | msgstr "대륙" 136 | 137 | #: models.py:72 138 | msgid "continents" 139 | msgstr "대륙" 140 | 141 | #: models.py:81 142 | msgid "code" 143 | msgstr "규약" 144 | 145 | #: models.py:83 146 | msgid "independence day" 147 | msgstr "독립 기념일" 148 | 149 | #: models.py:84 150 | msgid "gay friendly" 151 | msgstr "동성애자 친화적 인" 152 | 153 | #: models.py:89 models.py:103 154 | msgid "country" 155 | msgstr "국가" 156 | 157 | #: models.py:90 158 | msgid "countries" 159 | msgstr "국가" 160 | 161 | #: models.py:100 162 | msgid "is capital city" 163 | msgstr "수도이다" 164 | 165 | #: models.py:106 166 | msgid "city" 167 | msgstr "시티" 168 | 169 | #: models.py:107 170 | msgid "cities" 171 | msgstr "도시들" 172 | 173 | #: templates/integration/menu.html:3 174 | msgid "Cities" 175 | msgstr "도시들" 176 | 177 | #: templates/integration/menu.html:4 178 | msgid "Continents" 179 | msgstr "대륙" 180 | 181 | #: templates/integration/menu.html:5 182 | msgid "Countries" 183 | msgstr "국가" 184 | 185 | #: templates/integration/menu.html:6 186 | msgid "Oceans" 187 | msgstr "대양" 188 | 189 | #: templates/integration/menu.html:7 190 | msgid "Seas" 191 | msgstr "바다" 192 | -------------------------------------------------------------------------------- /demo/integration/locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/integration/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /demo/integration/locale/ru/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # distributed under the same license as the Django Material package. 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: django-material\n" 5 | "MIME-Version: 1.0\n" 6 | "Content-Type: text/plain; charset=UTF-8\n" 7 | "Content-Transfer-Encoding: 8bit\n" 8 | "Language: ru\n" 9 | "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" 10 | "%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" 11 | "%100>=11 && n%100<=14)? 2 : 3);\n" 12 | 13 | #: admin.py:45 14 | msgid "wiki" 15 | msgstr "вики" 16 | 17 | #: admin.py:83 18 | msgid "map" 19 | msgstr "карта" 20 | 21 | #: admin.py:87 22 | msgid "short description" 23 | msgstr "описание" 24 | 25 | #: admin.py:109 views.py:68 26 | msgid "sea area" 27 | msgstr "площадь моря" 28 | 29 | #: admin.py:120 views.py:21 30 | msgid "Details" 31 | msgstr "Подробности" 32 | 33 | #: admin.py:123 views.py:25 34 | msgid "Fun facts" 35 | msgstr "Забавные факты" 36 | 37 | #: admin.py:138 38 | msgid "surrounded oceans" 39 | msgstr "окружающие океаны" 40 | 41 | #: admin.py:142 42 | msgid "countries count" 43 | msgstr "количество стран" 44 | 45 | #: admin.py:182 admin.py:204 46 | msgid "became independent in XX century" 47 | msgstr "Обрела независимость в XX веке" 48 | 49 | #: apps.py:9 50 | msgid "CRUD sample" 51 | msgstr "CRUD пример" 52 | 53 | #: models.py:9 models.py:26 models.py:48 models.py:82 models.py:99 54 | msgid "name" 55 | msgstr "название" 56 | 57 | #: models.py:10 models.py:30 models.py:49 58 | msgid "area" 59 | msgstr "площадь" 60 | 61 | #: models.py:11 62 | msgid "slug" 63 | msgstr "" 64 | 65 | #: models.py:12 66 | msgid "description" 67 | msgstr "описание" 68 | 69 | #: models.py:13 70 | msgid "map url" 71 | msgstr "URL-адрес карты" 72 | 73 | #: models.py:16 models.py:28 74 | msgid "ocean" 75 | msgstr "океан" 76 | 77 | #: models.py:17 models.py:60 78 | msgid "oceans" 79 | msgstr "океаны" 80 | 81 | #: models.py:27 82 | msgid "parent" 83 | msgstr "родитель" 84 | 85 | #: models.py:30 86 | msgid "km²" 87 | msgstr "км²" 88 | 89 | #: models.py:31 90 | msgid "average depth" 91 | msgstr "средняя глубина" 92 | 93 | #: models.py:31 models.py:32 94 | msgid "meters" 95 | msgstr "метры" 96 | 97 | #: models.py:32 98 | msgid "maximum depth" 99 | msgstr "максимальная глубина" 100 | 101 | #: models.py:38 102 | msgid "sea" 103 | msgstr "море" 104 | 105 | #: models.py:39 106 | msgid "seas" 107 | msgstr "моря" 108 | 109 | #: models.py:50 models.py:101 110 | msgid "population" 111 | msgstr "население" 112 | 113 | #: models.py:51 114 | msgid "population density" 115 | msgstr "плотность населения" 116 | 117 | #: models.py:54 118 | msgid "largest country" 119 | msgstr "самая большая страна" 120 | 121 | #: models.py:56 122 | msgid "biggest city" 123 | msgstr "самый большой город" 124 | 125 | #: models.py:57 126 | msgid "longest river" 127 | msgstr "самая длинная река" 128 | 129 | #: models.py:58 130 | msgid "biggest mountain" 131 | msgstr "самая большая гора" 132 | 133 | #: models.py:71 models.py:86 134 | msgid "continent" 135 | msgstr "континент" 136 | 137 | #: models.py:72 138 | msgid "continents" 139 | msgstr "континенты" 140 | 141 | #: models.py:81 142 | msgid "code" 143 | msgstr "код" 144 | 145 | #: models.py:83 146 | msgid "independence day" 147 | msgstr "день независимости" 148 | 149 | #: models.py:84 150 | msgid "gay friendly" 151 | msgstr "дружелюбная" 152 | 153 | #: models.py:89 models.py:103 154 | msgid "country" 155 | msgstr "страна" 156 | 157 | #: models.py:90 158 | msgid "countries" 159 | msgstr "страны" 160 | 161 | #: models.py:100 162 | msgid "is capital city" 163 | msgstr "столица" 164 | 165 | #: models.py:106 166 | msgid "city" 167 | msgstr "город" 168 | 169 | #: models.py:107 170 | msgid "cities" 171 | msgstr "города" 172 | 173 | #: templates/integration/menu.html:3 174 | msgid "Cities" 175 | msgstr "Города" 176 | 177 | #: templates/integration/menu.html:4 178 | msgid "Continents" 179 | msgstr "Континенты" 180 | 181 | #: templates/integration/menu.html:5 182 | msgid "Countries" 183 | msgstr "Страны" 184 | 185 | #: templates/integration/menu.html:6 186 | msgid "Oceans" 187 | msgstr "Океаны" 188 | 189 | #: templates/integration/menu.html:7 190 | msgid "Seas" 191 | msgstr "Моря" 192 | -------------------------------------------------------------------------------- /demo/integration/locale/zh_Hans/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/integration/locale/zh_Hans/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /demo/integration/locale/zh_Hans/LC_MESSAGES/django.po: -------------------------------------------------------------------------------- 1 | # distributed under the same license as the Django Material package. 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: django-material\n" 5 | "Report-Msgid-Bugs-To: \n" 6 | "POT-Creation-Date: 2017-05-09 09:52+0000\n" 7 | "Language: zh_CN\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "Plural-Forms: nplurals=1; plural=0;\n" 12 | 13 | #: admin.py:45 14 | msgid "wiki" 15 | msgstr "维基" 16 | 17 | #: admin.py:83 18 | msgid "map" 19 | msgstr "地图" 20 | 21 | #: admin.py:87 22 | msgid "short description" 23 | msgstr "简短的介绍" 24 | 25 | #: admin.py:109 views.py:69 26 | msgid "sea area" 27 | msgstr "海域" 28 | 29 | #: admin.py:120 views.py:21 30 | msgid "Details" 31 | msgstr "细节" 32 | 33 | #: admin.py:123 views.py:25 34 | msgid "Fun facts" 35 | msgstr "有趣的事实" 36 | 37 | #: admin.py:138 38 | msgid "surrounded oceans" 39 | msgstr "周围的海洋" 40 | 41 | #: admin.py:142 42 | msgid "countries count" 43 | msgstr "国家数" 44 | 45 | #: admin.py:182 admin.py:204 views.py:49 46 | msgid "became independent in XX century" 47 | msgstr "在二十世纪成为独立的" 48 | 49 | #: apps.py:9 50 | msgid "CRUD sample" 51 | msgstr "CRUD样本" 52 | 53 | #: models.py:9 models.py:26 models.py:48 models.py:82 models.py:99 54 | msgid "name" 55 | msgstr "名称" 56 | 57 | #: models.py:10 models.py:30 models.py:49 58 | msgid "area" 59 | msgstr "区" 60 | 61 | #: models.py:11 62 | msgid "slug" 63 | msgstr "" 64 | 65 | #: models.py:12 66 | msgid "description" 67 | msgstr "描述" 68 | 69 | #: models.py:13 70 | msgid "map url" 71 | msgstr "地图网址" 72 | 73 | #: models.py:16 models.py:28 74 | msgid "ocean" 75 | msgstr "海洋" 76 | 77 | #: models.py:17 models.py:60 78 | msgid "oceans" 79 | msgstr "海洋" 80 | 81 | #: models.py:27 82 | msgid "parent" 83 | msgstr "起源" 84 | 85 | #: models.py:30 86 | msgid "km²" 87 | msgstr "平方公里" 88 | 89 | #: models.py:31 90 | msgid "average depth" 91 | msgstr "平均深度" 92 | 93 | #: models.py:31 models.py:32 94 | msgid "meters" 95 | msgstr "米" 96 | 97 | #: models.py:32 98 | msgid "maximum depth" 99 | msgstr "最大深度" 100 | 101 | #: models.py:38 102 | msgid "sea" 103 | msgstr "海" 104 | 105 | #: models.py:39 106 | msgid "seas" 107 | msgstr "海域" 108 | 109 | #: models.py:50 models.py:101 110 | msgid "population" 111 | msgstr "人口" 112 | 113 | #: models.py:51 114 | msgid "population density" 115 | msgstr "人口密度" 116 | 117 | #: models.py:54 118 | msgid "largest country" 119 | msgstr "最大的国家" 120 | 121 | #: models.py:56 122 | msgid "biggest city" 123 | msgstr "最大的城市" 124 | 125 | #: models.py:57 126 | msgid "longest river" 127 | msgstr "最长的河" 128 | 129 | #: models.py:58 130 | msgid "biggest mountain" 131 | msgstr "最大的山" 132 | 133 | #: models.py:71 models.py:86 134 | msgid "continent" 135 | msgstr "大陆" 136 | 137 | #: models.py:72 138 | msgid "continents" 139 | msgstr "大陆" 140 | 141 | #: models.py:81 142 | msgid "code" 143 | msgstr "码" 144 | 145 | #: models.py:83 146 | msgid "independence day" 147 | msgstr "独立日" 148 | 149 | #: models.py:84 150 | msgid "gay friendly" 151 | msgstr "同志友好" 152 | 153 | #: models.py:89 models.py:103 154 | msgid "country" 155 | msgstr "国家" 156 | 157 | #: models.py:90 158 | msgid "countries" 159 | msgstr "国家" 160 | 161 | #: models.py:100 162 | msgid "is capital city" 163 | msgstr "是首都" 164 | 165 | #: models.py:106 166 | msgid "city" 167 | msgstr "市" 168 | 169 | #: models.py:107 170 | msgid "cities" 171 | msgstr "城市" 172 | 173 | #: templates/integration/menu.html:3 174 | msgid "Cities" 175 | msgstr "城市" 176 | 177 | #: templates/integration/menu.html:4 178 | msgid "Continents" 179 | msgstr "大陆" 180 | 181 | #: templates/integration/menu.html:5 182 | msgid "Countries" 183 | msgstr "国家" 184 | 185 | #: templates/integration/menu.html:6 186 | msgid "Oceans" 187 | msgstr "海洋" 188 | 189 | #: templates/integration/menu.html:7 190 | msgid "Seas" 191 | msgstr "海域" 192 | -------------------------------------------------------------------------------- /demo/integration/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='City', 15 | fields=[ 16 | ('id', models.AutoField(primary_key=True, auto_created=True, serialize=False, verbose_name='ID')), 17 | ('name', models.CharField(max_length=250)), 18 | ('is_capital', models.BooleanField(default=False)), 19 | ('population', models.BigIntegerField()), 20 | ], 21 | options={ 22 | 'ordering': ['name'], 23 | 'verbose_name_plural': 'cities', 24 | }, 25 | ), 26 | migrations.CreateModel( 27 | name='Continent', 28 | fields=[ 29 | ('name', models.CharField(primary_key=True, serialize=False, max_length=250)), 30 | ('area', models.BigIntegerField(help_text='km²')), 31 | ('population', models.BigIntegerField()), 32 | ('population_density', models.DecimalField(decimal_places=2, max_digits=8)), 33 | ('longest_river', models.CharField(max_length=250, blank=True, null=True)), 34 | ('biggest_mountain', models.CharField(max_length=250, blank=True, null=True)), 35 | ('hemisphere', models.CharField(max_length=5, choices=[('NORTH', 'North'), ('SOUTH', 'South'), ('BOTH', 'Both')])), 36 | ('biggest_city', models.OneToOneField(on_delete=models.CASCADE, blank=True, null=True, to='integration.City')), 37 | ], 38 | options={ 39 | 'ordering': ['name'], 40 | }, 41 | ), 42 | migrations.CreateModel( 43 | name='Country', 44 | fields=[ 45 | ('id', models.AutoField(primary_key=True, auto_created=True, serialize=False, verbose_name='ID')), 46 | ('code', models.CharField(max_length=3, unique=True)), 47 | ('name', models.CharField(max_length=250)), 48 | ('independence_day', models.DateField(blank=True, null=True)), 49 | ('gay_friendly', models.NullBooleanField()), 50 | ('continent', models.ForeignKey(to='integration.Continent', on_delete=models.CASCADE, related_name='countries', null=True)), 51 | ], 52 | options={ 53 | 'ordering': ['name'], 54 | 'verbose_name_plural': 'countries', 55 | }, 56 | ), 57 | migrations.CreateModel( 58 | name='Ocean', 59 | fields=[ 60 | ('name', models.CharField(primary_key=True, serialize=False, max_length=250)), 61 | ('area', models.BigIntegerField()), 62 | ('slug', models.SlugField()), 63 | ('description', models.TextField()), 64 | ('map_url', models.URLField()), 65 | ], 66 | options={ 67 | 'ordering': ['name'], 68 | }, 69 | ), 70 | migrations.CreateModel( 71 | name='Sea', 72 | fields=[ 73 | ('id', models.AutoField(primary_key=True, auto_created=True, serialize=False, verbose_name='ID')), 74 | ('name', models.CharField(max_length=250)), 75 | ('area', models.BigIntegerField(help_text='km²')), 76 | ('avg_depth', models.IntegerField(help_text='meters', blank=True, null=True)), 77 | ('max_depth', models.IntegerField(help_text='meters', blank=True, null=True)), 78 | ('basin_countries', models.ManyToManyField(to='integration.Country', blank=True, related_name='seas')), 79 | ('ocean', models.ForeignKey(to='integration.Ocean', on_delete=models.CASCADE)), 80 | ('parent', models.ForeignKey(on_delete=models.CASCADE, blank=True, null=True, to='integration.Sea')), 81 | ], 82 | options={ 83 | 'ordering': ['name'], 84 | }, 85 | ), 86 | migrations.AddField( 87 | model_name='continent', 88 | name='largest_country', 89 | field=models.OneToOneField(to='integration.Country', on_delete=models.CASCADE, blank=True, null=True, related_name='+'), 90 | ), 91 | migrations.AddField( 92 | model_name='continent', 93 | name='oceans', 94 | field=models.ManyToManyField(to='integration.Ocean'), 95 | ), 96 | migrations.AddField( 97 | model_name='city', 98 | name='country', 99 | field=models.ForeignKey(to='integration.Country', on_delete=models.CASCADE, related_name='cities'), 100 | ), 101 | migrations.AlterUniqueTogether( 102 | name='city', 103 | unique_together=set([('name', 'country')]), 104 | ), 105 | ] 106 | -------------------------------------------------------------------------------- /demo/integration/migrations/0003_auto_20171128_1814.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.7 on 2017-11-28 10:14 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('integration', '0002_i18n'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='sea', 17 | name='area', 18 | field=models.BigIntegerField(help_text='平方公里', verbose_name='area'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /demo/integration/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/integration/migrations/__init__.py -------------------------------------------------------------------------------- /demo/integration/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.utils.encoding import python_2_unicode_compatible 3 | from django.utils.safestring import mark_safe 4 | from django.utils.translation import ugettext_lazy as _ 5 | 6 | 7 | @python_2_unicode_compatible 8 | class Ocean(models.Model): 9 | name = models.CharField(_('name'), max_length=250, primary_key=True) 10 | area = models.BigIntegerField(_('area')) 11 | slug = models.SlugField(_('slug')) 12 | description = models.TextField(_('description')) 13 | map_url = models.URLField(_('map url')) 14 | 15 | class Meta: 16 | verbose_name = _('ocean') 17 | verbose_name_plural = _('oceans') 18 | ordering = ['name'] 19 | 20 | def __str__(self): 21 | return self.name if self.name is not None else 'Ocean' 22 | 23 | 24 | @python_2_unicode_compatible 25 | class Sea(models.Model): 26 | name = models.CharField(_('name'), max_length=250) 27 | parent = models.ForeignKey('self', on_delete=models.CASCADE, blank=True, null=True, verbose_name=_('parent')) 28 | ocean = models.ForeignKey(Ocean, on_delete=models.CASCADE, verbose_name=_('ocean')) 29 | 30 | area = models.BigIntegerField(_('area'), help_text=mark_safe(_('km²'))) 31 | avg_depth = models.IntegerField(_('average depth'), help_text=_('meters'), null=True, blank=True) 32 | max_depth = models.IntegerField(_('maximum depth'), help_text=_('meters'), null=True, blank=True) 33 | 34 | basin_countries = models.ManyToManyField( 35 | 'Country', related_name='seas', blank=True) 36 | 37 | def get_parent_id_display(self): 38 | return self.parent 39 | 40 | class Meta: 41 | verbose_name = _('sea') 42 | verbose_name_plural = _('seas') 43 | ordering = ['name'] 44 | 45 | def __str__(self): 46 | return self.name 47 | 48 | 49 | @python_2_unicode_compatible 50 | class Continent(models.Model): 51 | name = models.CharField(_('name'), max_length=250, primary_key=True) 52 | area = models.BigIntegerField(_('area'), help_text=mark_safe('km²')) 53 | population = models.BigIntegerField(_('population')) 54 | population_density = models.DecimalField(_('population density'), decimal_places=2, max_digits=8) 55 | 56 | largest_country = models.OneToOneField( 57 | 'Country', on_delete=models.CASCADE, related_name='+', blank=True, null=True, verbose_name=_('largest country')) 58 | biggest_city = models.OneToOneField( 59 | 'City', on_delete=models.CASCADE, blank=True, null=True, verbose_name=_('biggest city')) 60 | longest_river = models.CharField(_('longest river'), max_length=250, blank=True, null=True) 61 | biggest_mountain = models.CharField(_('biggest mountain'), max_length=250, blank=True, null=True) 62 | 63 | oceans = models.ManyToManyField(Ocean, verbose_name=_('oceans')) 64 | hemisphere = models.CharField( 65 | max_length=5, choices=( 66 | ('NORTH', 'North'), 67 | ('SOUTH', 'South'), 68 | ('BOTH', 'Both'))) 69 | 70 | def __str__(self): 71 | return self.name if self.name is not None else 'Continent' 72 | 73 | class Meta: 74 | verbose_name = _('continent') 75 | verbose_name_plural = _('continents') 76 | ordering = ['name'] 77 | 78 | def countries_count(self): 79 | return self.countries.count() 80 | countries_count.short_description = _('countries count') 81 | 82 | 83 | @python_2_unicode_compatible 84 | class Country(models.Model): 85 | code = models.CharField(_('code'), max_length=3, unique=True) 86 | name = models.CharField(_('name'), max_length=250) 87 | independence_day = models.DateField(_('independence day'), null=True, blank=True) 88 | gay_friendly = models.NullBooleanField(_('gay friendly')) 89 | continent = models.ForeignKey( 90 | Continent, on_delete=models.CASCADE, null=True, related_name='countries', verbose_name=_('continent')) 91 | 92 | class Meta: 93 | verbose_name = _('country') 94 | verbose_name_plural = _('countries') 95 | ordering = ['name'] 96 | 97 | def __str__(self): 98 | return self.name 99 | 100 | 101 | @python_2_unicode_compatible 102 | class City(models.Model): 103 | name = models.CharField(_('name'), max_length=250) 104 | is_capital = models.BooleanField(_('is capital city'), default=False) 105 | population = models.BigIntegerField(_('population')) 106 | country = models.ForeignKey( 107 | Country, on_delete=models.CASCADE, related_name='cities', verbose_name=_('country')) 108 | 109 | class Meta: 110 | verbose_name = _('city') 111 | verbose_name_plural = _('cities') 112 | unique_together = ('name', 'country') 113 | ordering = ['name'] 114 | 115 | def __str__(self): 116 | return self.name 117 | -------------------------------------------------------------------------------- /demo/integration/templates/integration/base_module.html: -------------------------------------------------------------------------------- 1 | {% extends 'material/frontend/base_module.html' %} 2 | 3 | {% block extrahead %} 4 | 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /demo/integration/templates/integration/menu.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 9 | -------------------------------------------------------------------------------- /demo/integration/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url, include 2 | from django.views import generic 3 | 4 | from . import views 5 | 6 | urlpatterns = [ 7 | url('^$', generic.RedirectView.as_view(url='./city/'), name="index"), 8 | url('^city/', include(views.CityViewSet().urls)), 9 | url('^continent/', include(views.ContinentViewSet().urls)), 10 | url('^country/', include(views.CountryViewSet().urls)), 11 | url('^ocean/', include(views.OceanViewSet().urls)), 12 | url('^sea/', include(views.SeaViewSet().urls)), 13 | ] 14 | -------------------------------------------------------------------------------- /demo/integration/views.py: -------------------------------------------------------------------------------- 1 | from django.utils.translation import ugettext_lazy as _ 2 | 3 | from material import Layout, Row, Fieldset 4 | from material.frontend.views import ModelViewSet 5 | 6 | from . import models 7 | 8 | 9 | class CityViewSet(ModelViewSet): 10 | model = models.City 11 | ordering = ['-country', 'name'] 12 | list_display = ('name', 'country', 'population') 13 | 14 | 15 | class ContinentViewSet(ModelViewSet): 16 | model = models.Continent 17 | list_display = ( 18 | 'name', 'surrounded_oceans', 'countries_count', 19 | 'area', 'population', ) 20 | layout = Layout( 21 | 'name', 22 | Fieldset(_('Details'), 23 | 'area', 24 | Row('oceans', 'hemisphere'), 25 | Row('population', 'population_density')), 26 | Fieldset(_('Fun facts'), 27 | Row('largest_country', 'biggest_mountain'), 28 | Row('biggest_city', 'longest_river')) 29 | ) 30 | 31 | def surrounded_oceans(self, contintent): 32 | return ', '.join(ocean.name for ocean in contintent.oceans.all()) 33 | surrounded_oceans.short_description = _('surrounded oceans') 34 | 35 | 36 | class CountryViewSet(ModelViewSet): 37 | model = models.Country 38 | list_display = ( 39 | 'tld', 'name', 'continent', 40 | 'became_independent_in_20_century', 41 | 'gay_friendly') 42 | list_display_links = ('tld', 'name', ) 43 | 44 | def tld(self, country): 45 | return '.' + country.code.lower() 46 | tld.short_description = 'TLD' 47 | 48 | def became_independent_in_20_century(self, country): 49 | if country.independence_day: 50 | return 1900 <= country.independence_day.year <= 2000 51 | became_independent_in_20_century.short_description = _('became independent in XX century') 52 | 53 | 54 | class OceanViewSet(ModelViewSet): 55 | model = models.Ocean 56 | list_display = ('name', 'area', ) 57 | 58 | 59 | class SeaViewSet(ModelViewSet): 60 | model = models.Sea 61 | list_display = ('name', 'parent', 'ocean', 'sea_area', ) 62 | layout = Layout( 63 | Row('name', 'parent'), 64 | 'ocean', 65 | Row('area', 'avg_depth', 'max_depth'), 66 | 'basin_countries' 67 | ) 68 | 69 | def sea_area(self, sea): 70 | return None if sea.area == 0 else sea.area 71 | sea_area.short_description = _('sea area') 72 | -------------------------------------------------------------------------------- /demo/leave/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'demo.leave.apps.LeaveConfig' -------------------------------------------------------------------------------- /demo/leave/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import * 3 | # Register your models here. 4 | @admin.register(Leave) 5 | class LeaveAdmin(admin.ModelAdmin): 6 | list_display = ('req_by', 'req_date', 'resion') 7 | list_filter = ('req_by','depart_name','req_class') 8 | search_fields = ('resion',) 9 | 10 | @admin.register(LeaveProcess) 11 | class LeaveProcessAdmin(admin.ModelAdmin): 12 | list_display = ('req_by','req_date', 'resion', 'dep_approved', 'hr_approved') 13 | def req_by(self, LeaveProcess): 14 | return LeaveProcess.leave.req_by 15 | def req_date(self, LeaveProcess): 16 | return LeaveProcess.leave.req_date 17 | def resion(self, LeaveProcess): 18 | return LeaveProcess.leave.resion 19 | 20 | @admin.register(Leave_class) 21 | class Leave_classAdmin(admin.ModelAdmin): 22 | pass -------------------------------------------------------------------------------- /demo/leave/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | from material.frontend.apps import ModuleMixin 3 | from django.utils.translation import ugettext_lazy as _ 4 | 5 | 6 | class LeaveConfig(AppConfig,ModuleMixin): 7 | name = 'demo.leave' 8 | icon = 'backup' 9 | verbose_name = _("请假") 10 | 11 | 12 | -------------------------------------------------------------------------------- /demo/leave/flows.py: -------------------------------------------------------------------------------- 1 | from viewflow import flow, frontend 2 | from viewflow.base import this, Flow 3 | from viewflow.flow.views import CreateProcessView, UpdateProcessView 4 | from viewflow.flow import views as flow_views 5 | 6 | from .models import * 7 | from .views import * 8 | 9 | @frontend.register 10 | class LeaveFlow(Flow): 11 | process_class = LeaveProcess 12 | summary_template = "{{ process.leave.req_by.user.username }}的请假" 13 | process_title = '请假' 14 | 15 | start = ( 16 | flow.Start( 17 | LeaveStartView 18 | ).Permission( 19 | auto_create=True 20 | ).Next(this.dep_approve) 21 | ) 22 | 23 | dep_approve = ( 24 | flow.View( 25 | approve_check 26 | ).Assign( 27 | #提交到自己的manager 28 | lambda act: act.process.leave.req_by.Manager.user 29 | ).Next( 30 | this.check_dep_approve) 31 | ) 32 | 33 | check_dep_approve = ( 34 | flow.If(lambda activation: activation.process.dep_approved==1) 35 | .Then(this.hr_approve) 36 | .Else(this.NG) 37 | ) 38 | 39 | hr_approve = ( 40 | flow.View( 41 | approve_check 42 | ).Permission( 43 | #有权限就可以签收,给特定的人赋权就可以。 44 | auto_create=True 45 | ).Next( 46 | this.check_hr_approve 47 | ) 48 | ) 49 | 50 | check_hr_approve = ( 51 | flow.If(lambda activation: activation.process.hr_approved==1) 52 | .Then(this.OK) 53 | .Else(this.NG) 54 | ) 55 | NG = ( 56 | flow.Handler( 57 | this.send_NG_request 58 | ).Next(this.end) 59 | ) 60 | OK = ( 61 | flow.Handler( 62 | this.send_OK_request 63 | ).Next(this.end) 64 | ) 65 | 66 | end = flow.End() 67 | 68 | def send_NG_request(self, activation): 69 | print('dep_approved==>'+ str(activation.process.dep_approved)) 70 | print('hr_approved==>' + str(activation.process.hr_approved)) 71 | print('NG') 72 | 73 | def send_OK_request(self, activation): 74 | #print(activation.process.leave.user) 75 | print('OK') -------------------------------------------------------------------------------- /demo/leave/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from material import * 3 | 4 | from . import models 5 | 6 | 7 | class LeaveForm(forms.ModelForm): 8 | def __init__(self, *args, **kwargs): 9 | super(LeaveForm, self).__init__(*args, **kwargs) 10 | instance = getattr(self, 'instance', None) 11 | #不起作用 12 | if instance and instance.id: 13 | self.fields['req_by'].widget.attrs['disabled'] = True 14 | self.fields['position'].widget.attrs['readonly'] = True 15 | self.fields['depart_name'].widget.attrs['disabled'] = True 16 | self.fields['req_date'].widget.attrs['disabled'] = True 17 | 18 | 19 | class Meta: 20 | model = models.Leave 21 | fields = ['req_by','depart_name','position','req_date','req_class','file_url','start_time','end_time','resion',] 22 | widgets = { 23 | 'password': forms.PasswordInput() 24 | } 25 | 26 | class LeaveDepCheckForm(forms.ModelForm): 27 | 28 | def __init__(self, *args, **kwargs): 29 | super(LeaveDepCheckForm, self).__init__(*args, **kwargs) 30 | instance = getattr(self, 'instance', None) 31 | if instance and instance.id: 32 | self.fields['position'].widget.attrs['readonly'] = True 33 | self.fields['resion'].widget.attrs['readonly'] = True 34 | self.fields['file_url'].widget.attrs['readonly'] = True 35 | self.fields['req_by'].widget.attrs['disabled'] = True 36 | self.fields['depart_name'].widget.attrs['disabled'] = True 37 | self.fields['req_date'].widget.attrs['disabled'] = True 38 | self.fields['start_time'].widget.attrs['disabled'] = True 39 | self.fields['end_time'].widget.attrs['disabled'] = True 40 | self.fields['req_class'].widget.attrs['disabled'] = True 41 | 42 | 43 | layout = Layout( 44 | Row(Span2('req_by'), 'depart_name', Span2('position'),'req_date'), 45 | Row('req_class', 'file_url'), 46 | Row('start_time','end_time'), 47 | 'resion', 48 | 'dep_approved', 49 | 'dep_approved_comment', 50 | ) 51 | dep_approved =forms.ChoiceField(label='部门审核',choices=((1, "同意"), 52 | (-1, "不同意"),)) 53 | dep_approved_comment = forms.CharField(label='审核意见',widget=forms.Textarea) 54 | 55 | class Meta: 56 | model = models.Leave 57 | fields = ['req_by','depart_name', 'position', 'req_date', 'req_class', 'file_url', 'start_time', 'end_time', 'resion', 'dep_approved','dep_approved_comment'] 58 | 59 | 60 | 61 | class LeaveHrCheckForm(LeaveDepCheckForm): 62 | 63 | class Meta: 64 | model = models.Leave 65 | fields = ['req_by','depart_name', 'position', 'req_date', 'req_class', 'file_url', 'start_time', 'end_time', 'resion', 'dep_approved','dep_approved_comment','hr_approved','hr_approved_comment'] 66 | 67 | 68 | layout = Layout( 69 | Row(Span2('req_by'), 'depart_name', Span2('position'),'req_date'), 70 | Row('req_class', 'file_url'), 71 | Row('start_time','end_time'), 72 | 'resion', 73 | 'dep_approved', 74 | 'dep_approved_comment', 75 | 'hr_approved', 76 | 'hr_approved_comment', 77 | ) 78 | 79 | hr_approved =forms.ChoiceField(label='人事审核',choices=((1, "同意"), 80 | (-1, "不同意"),)) 81 | hr_approved_comment = forms.CharField(label='审核意见',widget=forms.Textarea) 82 | 83 | def __init__(self, *args, **kwargs): 84 | super(LeaveHrCheckForm, self).__init__(*args, **kwargs) 85 | instance = getattr(self, 'instance', None) 86 | if instance and instance.id: 87 | self.fields['dep_approved'].widget.attrs['disabled'] = True 88 | self.fields['dep_approved_comment'].widget.attrs['readonly'] = True -------------------------------------------------------------------------------- /demo/leave/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.7 on 2017-11-24 07:47 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | import django.db.models.deletion 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ('viewflow', '0006_i18n'), 15 | ('hr', '0001_initial'), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='Leave', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('req_date', models.DateTimeField(verbose_name='申请时间')), 24 | ('position', models.CharField(max_length=256, verbose_name='职位')), 25 | ('start_time', models.DateTimeField(verbose_name='开始时间')), 26 | ('end_time', models.DateTimeField(verbose_name='结束时间')), 27 | ('resion', models.CharField(max_length=256, verbose_name='请假事由')), 28 | ('file_url', models.CharField(max_length=256, verbose_name='上传附件')), 29 | ('depart_name', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hr.department', verbose_name='部门')), 30 | ('req_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hr.employee', verbose_name='申请人')), 31 | ], 32 | options={ 33 | 'verbose_name': '请假', 34 | 'verbose_name_plural': '请假', 35 | 'db_table': 'leave', 36 | }, 37 | ), 38 | migrations.CreateModel( 39 | name='Leave_class', 40 | fields=[ 41 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 42 | ('name', models.CharField(max_length=256, verbose_name='名称')), 43 | ], 44 | options={ 45 | 'verbose_name': '请假类别', 46 | 'verbose_name_plural': '请假类别', 47 | 'db_table': 'leave_class', 48 | }, 49 | ), 50 | migrations.CreateModel( 51 | name='LeaveProcess', 52 | fields=[ 53 | ('process_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='viewflow.Process')), 54 | ('dep_approved', models.BooleanField(default=False, verbose_name='部门审核')), 55 | ('dep_approved_time', models.DateTimeField(blank=True, null=True, verbose_name='部门领导审核时间')), 56 | ('dep_approved_comment', models.CharField(blank=True, max_length=256, null=True, verbose_name='部门领导审核意见')), 57 | ('hr_approved', models.BooleanField(default=False, verbose_name='人事审核')), 58 | ('hr_approved_time', models.DateTimeField(blank=True, null=True, verbose_name='人事审核时间')), 59 | ('hr_approved_comment', models.CharField(blank=True, max_length=256, null=True, verbose_name='人事审核意见')), 60 | ('comment', models.CharField(blank=True, max_length=1024, null=True)), 61 | ('leave', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='leave.Leave')), 62 | ], 63 | options={ 64 | 'verbose_name': '请假过程', 65 | 'verbose_name_plural': '请假过程', 66 | 'db_table': 'leave_process', 67 | }, 68 | bases=('viewflow.process',), 69 | ), 70 | migrations.AddField( 71 | model_name='leave', 72 | name='req_class', 73 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='leave.Leave_class', verbose_name='请假类别'), 74 | ), 75 | ] 76 | -------------------------------------------------------------------------------- /demo/leave/migrations/0002_leave_comment.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.7 on 2017-11-29 09:41 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('leave', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='leave', 17 | name='comment', 18 | field=models.CharField(blank=True, max_length=256, null=True, verbose_name='备注'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /demo/leave/migrations/0003_auto_20171130_0922.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.7 on 2017-11-30 01:22 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('leave', '0002_leave_comment'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AlterField( 16 | model_name='leaveprocess', 17 | name='dep_approved', 18 | field=models.IntegerField(default=0, verbose_name='部门审核'), 19 | ), 20 | migrations.AlterField( 21 | model_name='leaveprocess', 22 | name='hr_approved', 23 | field=models.IntegerField(default=0, verbose_name='人事审核'), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /demo/leave/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/leave/migrations/__init__.py -------------------------------------------------------------------------------- /demo/leave/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from viewflow.models import Process 3 | from demo.hr.models import * 4 | 5 | 6 | class Leave_class(models.Model): 7 | name= models.CharField(max_length=256, verbose_name=u"名称") 8 | class Meta: 9 | db_table = 'leave_class' 10 | verbose_name = '请假类别' 11 | verbose_name_plural = "请假类别" 12 | 13 | 14 | 15 | def __str__(self): 16 | return self.name 17 | 18 | 19 | # class department(models.Model): 20 | # name = models.CharField(max_length=256, verbose_name=u"名称") 21 | # leader = models.ForeignKey(User, verbose_name=u"部门经理") 22 | # 23 | # class Meta: 24 | # db_table = 'department' 25 | # verbose_name = '部门' 26 | # verbose_name_plural = "部门" 27 | # 28 | # def __str__(self): 29 | # return self.name 30 | 31 | class Leave(models.Model): 32 | """请假内容""" 33 | req_by = models.ForeignKey(employee, verbose_name=u"申请人") 34 | req_date = models.DateTimeField(verbose_name=u"申请时间") 35 | depart_name = models.ForeignKey(department,verbose_name=u'部门') 36 | position = models.CharField(max_length=256, verbose_name=u"职位") 37 | req_class =models.ForeignKey(Leave_class,verbose_name=u'请假类别') 38 | start_time=models.DateTimeField(verbose_name=u"开始时间") 39 | end_time=models.DateTimeField(verbose_name=u"结束时间") 40 | resion=models.CharField(max_length=256, verbose_name=u"请假事由") 41 | file_url=models.CharField(max_length=256, verbose_name=u"上传附件") 42 | comment = models.CharField(max_length=256, verbose_name=u"备注",blank=True,null=True) 43 | #审批领导 44 | 45 | class Meta: 46 | db_table = 'leave' 47 | verbose_name = '请假' 48 | verbose_name_plural = "请假" 49 | 50 | def __str__(self): 51 | return self.req_by.user.username 52 | 53 | 54 | class LeaveProcess(Process): 55 | 56 | leave = models.OneToOneField(Leave,null=True,blank=True) 57 | 58 | dep_approved = models.IntegerField(default=0, verbose_name=u"部门审核") 59 | dep_approved_time = models.DateTimeField(verbose_name=u"部门领导审核时间",blank=True,null=True) 60 | dep_approved_comment = models.CharField(max_length=256, verbose_name=u"部门领导审核意见",blank=True,null=True) 61 | 62 | hr_approved = models.IntegerField(default=0, verbose_name=u"人事审核") 63 | hr_approved_time = models.DateTimeField(verbose_name=u"人事审核时间",blank=True,null=True) 64 | hr_approved_comment= models.CharField(max_length=256, verbose_name=u"人事审核意见",blank=True,null=True) 65 | comment=models.CharField(max_length=1024,blank=True,null=True) 66 | 67 | class Meta: 68 | db_table = 'leave_process' 69 | verbose_name = '请假过程' 70 | verbose_name_plural = "请假过程" 71 | -------------------------------------------------------------------------------- /demo/leave/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /demo/leave/urls.py: -------------------------------------------------------------------------------- 1 | from .views import * 2 | from .flows import LeaveFlow 3 | from django.conf.urls import url, include 4 | from django.views import generic 5 | 6 | 7 | urlpatterns = [ 8 | url('^$', generic.RedirectView.as_view(url='./leave/'), name="index"), 9 | url('^leave/', include(LeaveFlowViewSet(LeaveFlow).urls),name="leave"), 10 | url('^leave/action/delete/(?P\d+)/$',LeaveDel, name="delete"), 11 | ] 12 | -------------------------------------------------------------------------------- /demo/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | import django 3 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 4 | 5 | 6 | # Quick-start development settings - unsuitable for production 7 | # See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/ 8 | 9 | # SECURITY WARNING: keep the secret key used in production secret! 10 | SECRET_KEY = 'ratn!684yf7ewt-%j%afwf7et9c=!oan$=w6#)fn#4u$ie4!as' 11 | 12 | # SECURITY WARNING: don't run with debug turned on in production! 13 | DEBUG = True 14 | 15 | ALLOWED_HOSTS = [] 16 | 17 | INTERNAL_IPS = [ 18 | '127.0.0.1' 19 | ] 20 | 21 | # Application definition 22 | 23 | INSTALLED_APPS = ( 24 | # viewflow 25 | 'viewflow.frontend', 26 | 'viewflow', 27 | 28 | # material 29 | 'material', 30 | 'material.frontend', 31 | 'material.admin', 32 | 33 | # django 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'demo.bloodtest', 41 | 'demo.customnode', 42 | 'demo.leave', 43 | 'demo.hr', 44 | 'demo.countersign', 45 | 'demo.integration', 46 | 'demo.employees', 47 | 'demo.testing', 48 | #'demo.shipment', 49 | ) 50 | 51 | MIDDLEWARE_CLASSES = ( 52 | 'django.contrib.sessions.middleware.SessionMiddleware', 53 | 'django.middleware.locale.LocaleMiddleware', 54 | 'django.middleware.common.CommonMiddleware', 55 | 'django.middleware.csrf.CsrfViewMiddleware', 56 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 57 | 'django.contrib.messages.middleware.MessageMiddleware', 58 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 59 | 'material.frontend.middleware.SmoothNavigationMiddleware', 60 | ) 61 | 62 | ROOT_URLCONF = 'demo.urls' 63 | 64 | LOGIN_REDIRECT_URL = '/workflow/' 65 | 66 | # Database 67 | # https://docs.djangoproject.com/en/1.6/ref/settings/#databases 68 | import dj_database_url # NOQA 69 | 70 | DATABASES = { 71 | 'default': dj_database_url.config() or { 72 | 'ENGINE': 'django.db.backends.sqlite3', 73 | 'NAME': os.path.join(BASE_DIR, 'db{}{}.sqlite3'.format(*django.VERSION[:2])), 74 | } 75 | } 76 | 77 | # Templates 78 | 79 | TEMPLATES = [ 80 | { 81 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 82 | 'DIRS': [ 83 | os.path.join(BASE_DIR, 'demo/templates'), 84 | ], 85 | 'APP_DIRS': True, 86 | 'OPTIONS': { 87 | 'context_processors': [ 88 | 'django.template.context_processors.debug', 89 | 'django.template.context_processors.request', 90 | 'django.contrib.auth.context_processors.auth', 91 | 'django.contrib.messages.context_processors.messages', 92 | 'django.template.context_processors.request', 93 | 'demo.website.users', 94 | 'demo.website.testing_types', 95 | 'material.frontend.context_processors.modules', 96 | ], 97 | 'debug': True, 98 | }, 99 | }, 100 | ] 101 | 102 | # Internationalization 103 | # https://docs.djangoproject.com/en/1.6/topics/i18n/ 104 | 105 | LANGUAGE_CODE = 'zh-hans' 106 | 107 | # TIME_ZONE = 'UTC' 108 | TIME_ZONE = 'Asia/Shanghai' 109 | 110 | USE_I18N = True 111 | 112 | USE_L10N = True 113 | 114 | USE_TZ = True 115 | 116 | # South 117 | if django.VERSION < (1, 7): 118 | INSTALLED_APPS += ('south', ) 119 | 120 | # Static files (CSS, JavaScript, Images) 121 | # https://docs.djangoproject.com/en/1.6/howto/static-files/ 122 | 123 | STATIC_URL = '/static/' 124 | 125 | STATICFILES_DIRS = [ 126 | os.path.join(BASE_DIR, "demo", "static"), 127 | ] 128 | 129 | try: 130 | from demo.local_settings import * # NOQA 131 | except ImportError: 132 | pass 133 | -------------------------------------------------------------------------------- /demo/static/demo/css/vfOA.css: -------------------------------------------------------------------------------- 1 | span.approve_ok_icon{height:20px; width:20px; display:block; position:relative;} 2 | span.approve_ok_icon:after, .approve_ok_icon:before{content:''; height:14px; width:3px; display:block; background:#333; position:absolute; top:3px; left:10px; border-radius:5px;-webkit-border-radius:5px;-moz-border-radius:5px; transform:rotate(45deg);-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-o-transform:rotate(45deg);-ms-transform:rotate(45deg);} 3 | span.approve_ok_icon:before{height:6px; transform:rotate(-45deg);-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-o-transform:rotate(-45deg);-ms-transform:rotate(-45deg); top:9px; left:4px;} 4 | span.approve_ng_icon{height:20px; width:20px; display:block; position:relative;} 5 | span.approve_ng_icon:before, .approve_ng_icon:after{content:''; height:4px; width:20px; display:block; background:#333; border-radius:5px;-webkit-border-radius:5px;-moz-border-radius:5px; position:absolute; top:8px; left:0px; transform:rotate(-45deg);-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-o-transform:rotate(-45deg);-ms-transform:rotate(-45deg);} 6 | span.approve_ng_icon:after{transform:rotate(45deg);-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-o-transform:rotate(45deg);-ms-transform:rotate(45deg);} -------------------------------------------------------------------------------- /demo/static/demo/js/jquery.cookie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Cookie Plugin v1.4.1 3 | * https://github.com/carhartl/jquery-cookie 4 | * 5 | * Copyright 2013 Klaus Hartl 6 | * Released under the MIT license 7 | */ 8 | !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){function b(a){return h.raw?a:encodeURIComponent(a)}function c(a){return h.raw?a:decodeURIComponent(a)}function d(a){return b(h.json?JSON.stringify(a):String(a))}function e(a){0===a.indexOf('"')&&(a=a.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\"));try{return a=decodeURIComponent(a.replace(g," ")),h.json?JSON.parse(a):a}catch(b){}}function f(b,c){var d=h.raw?b:e(b);return a.isFunction(c)?c(d):d}var g=/\+/g,h=a.cookie=function(e,g,i){if(void 0!==g&&!a.isFunction(g)){if(i=a.extend({},h.defaults,i),"number"==typeof i.expires){var j=i.expires,k=i.expires=new Date;k.setTime(+k+864e5*j)}return document.cookie=[b(e),"=",d(g),i.expires?"; expires="+i.expires.toUTCString():"",i.path?"; path="+i.path:"",i.domain?"; domain="+i.domain:"",i.secure?"; secure":""].join("")}for(var l=e?void 0:{},m=document.cookie?document.cookie.split("; "):[],n=0,o=m.length;o>n;n++){var p=m[n].split("="),q=c(p.shift()),r=p.join("=");if(e&&e===q){l=f(r,g);break}e||void 0===(r=f(r))||(l[q]=r)}return l};h.defaults={},a.removeCookie=function(b,c){return void 0===a.cookie(b)?!1:(a.cookie(b,"",a.extend({},c,{expires:-1})),!a.cookie(b))}}); -------------------------------------------------------------------------------- /demo/templates/demo/base_module.html: -------------------------------------------------------------------------------- 1 | {% extends 'material/frontend/base_module.html' %} 2 | {% load static viewflow_frontend %} 3 | {% block extracss %} 4 | 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /demo/templates/demo/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'demo/base_module.html' %} 2 | 3 | {% block content %} 4 |
5 |
6 |
7 |

vfOA demo

8 | Viewflow Source code on Github 9 |
10 | vfOA Source code on Github 11 |
    12 |
  • 13 | 请假 -一个简单的请假示例 14 |
  • 15 |
  • 16 | 会签 - 多人并行会签 17 |
  • 18 |
  • 19 | Dynamic split - 可动态设置拆分节点的个数,很实用。 20 |
  • 21 |
  • 22 | bloodtest - 自定义UI示例,wizard 多页面。 23 |
  • 24 |
25 | {% if not user.is_authenticated %}Please, log in to continue...{% endif %} 26 |
27 |
28 |
29 |
30 |
VfOA
31 |

基于Viewflow的OA演示系统,能够快速实现数据的CURD以及流程处理,可开发轻量级OA/CRM/ERP等系统

32 |
33 |
34 | 35 |
36 | {% endblock content %} 37 | -------------------------------------------------------------------------------- /demo/templates/hr/menu.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/templates/leave/check.html: -------------------------------------------------------------------------------- 1 | {% extends 'viewflow/flow/task.html' %} -------------------------------------------------------------------------------- /demo/templates/leave/menu.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/templates/leave/start.html: -------------------------------------------------------------------------------- 1 | {% extends 'viewflow/flow/start.html' %} 2 | {% load viewflow material_form material_frontend %} 3 | {% load i18n viewflow material_form %} 4 | 5 | {% block left-panel %} 6 |
7 |
8 |
9 |
10 |
11 | {{ activation.task.summary|default:activation.task.flow_task }} 12 |
{{ activation.process.summary }}
13 |
14 | {% block task_form %} 15 | {% csrf_token %} 16 | {% form form=form %} 17 | {# 修改field {% part form.position prefix %}
  • 1
  • 2
{% endpart %}#} 18 | {% endform %} 19 | {{ activation.management_form }} 20 | {% endblock %} 21 |
22 |
23 |
24 | {% block task_actions %} 25 |
26 | 27 |
28 | {% endblock %} 29 |
30 |
31 |
32 |
33 |
34 | {% endblock %} 35 | -------------------------------------------------------------------------------- /demo/templates/material/fields/_django_rangeinput.html: -------------------------------------------------------------------------------- 1 | {% load material_form material_form_internal l10n %} 2 | {% part bound_field.field %}
3 | 7 | {% part field label %} 8 | {{ bound_field.label }} 12 | {% endpart %} 13 | {% part field prefix %}{% endpart %}{% part field control %} 14 |

15 |

{% endpart %} 22 | {% part field help_text %}{% if field.help_text %} 23 |
{{ bound_field.help_text|safe }}
24 | {% endif %} 25 | {% endpart %}{% part field errors %} 26 | {% if bound_field.errors %}{% include 'material/field_errors.html' %}{% endif %} 27 | {% endpart %}{{ hidden_initial }} 28 |
29 | {% endpart %} 30 | -------------------------------------------------------------------------------- /demo/templates/material/fields/django_checkboxinput.html: -------------------------------------------------------------------------------- 1 | {% load material_form material_form_internal %} 2 | {% part bound_field.field %}
3 | 7 | {% part field prefix %}{% endpart %}{% part field control %} 8 | {% endpart %}{% part field label %} 15 | {{ bound_field.label }}{% endpart %} 18 | {% part field help_text %}{% part field errors %} 19 | {% if bound_field.errors %} 20 | {% include 'material/field_errors.html' %} 21 | {% endif %}{% endpart %}{% if field.help_text %} 22 |
{{ bound_field.help_text|safe }}
23 | {% endif %} 24 | {% endpart %}{{ hidden_initial }} 25 |
26 | {% endpart %} 27 | -------------------------------------------------------------------------------- /demo/templates/material/fields/django_checkboxselectmultiple.html: -------------------------------------------------------------------------------- 1 | {% load l10n material_form material_form_internal %} 2 | {% part field columns asvar 'columns' %}1{% endpart %} 3 | {% part bound_field.field %}
4 | {% part field label %}
5 | {{ bound_field.label }} 8 |
{% endpart %} 9 | 13 | {% part field prefix %}{% endpart %}{% part field control %} 14 |
15 | {% for group, items in bound_field|select_options %}{% for col_span, choices in items|split_choices_by_columns:4 %}
16 | {% for choice, value, selected, position in choices %} 17 |
18 | 26 | 27 |
28 | {% endfor %}
{% endfor %}{% endfor %} 29 |
30 | {% endpart %}{% part field help_text %}{% if field.help_text %} 31 |
{{ bound_field.help_text|safe }}
32 | {% endif %}{% part field errors %} 33 | {% if bound_field.errors %} 34 | {% include 'material/field_errors.html' %} 35 | {% endif %}{% endpart %} 36 | {% endpart %}{{ hidden_initial }} 37 |
38 | {% endpart %} 39 | -------------------------------------------------------------------------------- /demo/templates/material/fields/django_clearablefileinput.html: -------------------------------------------------------------------------------- 1 | {% load material_form material_form_internal i18n %} 2 | 3 | {% render bound_field template='fields/django_fileinput.html' %} 4 | {% part bound_field suffix %} 5 | {% if bound_field.value|is_initial_file and not field.required %} 6 |
7 | 8 | 9 |
10 | {% endif %} 11 | {% endpart %} 12 | {% endrender %} 13 | -------------------------------------------------------------------------------- /demo/templates/material/fields/django_dateinput.html: -------------------------------------------------------------------------------- 1 | {% load i18n material_form material_form_internal %} 2 | {% get_current_language as LANGUAGE_CODE %} 3 | {% get_language_info for LANGUAGE_CODE as lang %} 4 | {% part bound_field.field %}
5 | 9 | {% part field prefix %}{% endpart %}{% part field control %} 10 | 20 | {% endpart %} 21 | {% part field label %} 22 | {{ bound_field.label }} 26 | {% endpart %} 27 | {% part field help_text %}{% if field.help_text %} 28 |
{{ bound_field.help_text|safe }}
29 | {% endif %} 30 | {% endpart %}{% part field errors %} 31 | {% if bound_field.errors %} 32 | {% include 'material/field_errors.html' %} 33 | {% endif %}{% endpart %}{{ hidden_initial }} 34 |
35 | {% endpart %} 36 | -------------------------------------------------------------------------------- /demo/templates/material/fields/django_datetimeinput.html: -------------------------------------------------------------------------------- 1 | {% load i18n material_form material_form_internal %} 2 | {% get_current_language as LANGUAGE_CODE %} 3 | {% get_language_info for LANGUAGE_CODE as lang %} 4 | {% part bound_field.field %}
5 | 9 | {% part field prefix %}{% endpart %}{% part field control %} 10 | 20 | {% endpart %} 21 | {% part field label %} 22 | {{ bound_field.label }} 26 | {% endpart %} 27 | {% part field help_text %}{% if field.help_text %} 28 |
{{ bound_field.help_text|safe }}
29 | {% endif %} 30 | {% endpart %}{% part field errors %} 31 | {% if bound_field.errors %} 32 | {% include 'material/field_errors.html' %} 33 | {% endif %}{% endpart %}{{ hidden_initial }} 34 |
35 | {% endpart %} 36 | -------------------------------------------------------------------------------- /demo/templates/material/fields/django_fileinput.html: -------------------------------------------------------------------------------- 1 | {% load material_form material_form_internal i18n %} 2 | {% part bound_field.field %}
3 | 7 |
8 | {% part field prefix %}{% trans 'File' %}{% endpart %} 9 | 10 |
11 | {% part field suffix %}{% endpart %}{% part field control %}
12 | 19 |
{% endpart %} 20 | {% part field help_text %} 21 | {% if field.help_text %} 22 |
{{ bound_field.help_text|safe }}
23 | {% endif %} 24 | {% endpart %}{% part field errors %} 25 | {% if bound_field.errors %} 26 | {% include 'material/field_errors.html' %} 27 | {% endif %}{% endpart %}{{ hidden_initial }} 28 |
29 | {% endpart %} 30 | -------------------------------------------------------------------------------- /demo/templates/material/fields/django_input.html: -------------------------------------------------------------------------------- 1 | {% load material_form material_form_internal l10n %} 2 | {% if field.widget.input_type == 'range' %}{% include 'material/fields/_django_rangeinput.html'%}{% else %} 3 | {% part bound_field.field %}
4 | 8 | {% part field prefix %}{% endpart %}{% part field control %} 9 | {% endpart %} 16 | {% part field label %} 17 | {{ bound_field.label }} 21 | {% endpart %} 22 | {% part field help_text %}{% if field.help_text %} 23 |
{{ bound_field.help_text|safe }}
24 | {% endif %} 25 | {% endpart %}{% part field errors %} 26 | {% if bound_field.errors %}{% include 'material/field_errors.html' %}{% endif %} 27 | {% endpart %}{{ hidden_initial }} 28 |
29 | {% endpart %}{% endif %} 30 | -------------------------------------------------------------------------------- /demo/templates/material/fields/django_nullbooleanselect.html: -------------------------------------------------------------------------------- 1 | {% load material_form material_form_internal i18n %} 2 | 3 | {% render bound_field template='fields/django_select.html' %} 4 | {% part field options %}{% for value, choice in field.widget.choices %} 5 | {% endfor %}{% endpart %} 6 | {% endrender %} 7 | -------------------------------------------------------------------------------- /demo/templates/material/fields/django_radioselect.html: -------------------------------------------------------------------------------- 1 | {% load l10n material_form material_form_internal %} 2 | {% part bound_field.field %}
3 |
4 | {% part field prefix %}{% endpart %}{% part field label %}{% endpart %} 5 |
6 | 10 | {% part field control %} 11 | {% for group, items in bound_field|select_options %}{% for choice, value, selected in items %}
12 | 20 | {{ choice }} 24 |
25 | {% endfor %}{% endfor %}{% endpart %}{% part field help_text %}{% if field.help_text %} 26 |
{{ bound_field.help_text|safe }}
27 | {% endif %}{% part field errors %} 28 | {% if bound_field.errors %} 29 | {% include 'material/field_errors.html' %} 30 | {% endif %}{% endpart %} 31 | {% endpart %}{{ hidden_initial }} 32 |
33 | {% endpart %} 34 | -------------------------------------------------------------------------------- /demo/templates/material/fields/django_select.html: -------------------------------------------------------------------------------- 1 | {% load l10n material_form material_form_internal %} 2 | {% part bound_field.field %}
3 | 7 | {% part field prefix %}{% endpart %}{% part field label %} 8 | {{ bound_field.label }} 11 | {% endpart %} 12 | {% part field control %} 13 | 18 | {% part field options %}{% for group, items in bound_field|select_options %} 19 | {% if group %}{% endif %}{% for choice, value, selected in items %} 20 | {% endfor %} 21 | {% if group %}{% endif %}{% endfor %}{% endpart %} 22 | 23 | {% endpart %} 24 | {% part field help_text %}{% if field.help_text %} 25 |
{{ bound_field.help_text|safe }}
26 | {% endif %}{% endpart %}{% part field errors %} 27 | {% if bound_field.errors %} 28 | {% include 'material/field_errors.html' %} 29 | {% endif %}{% endpart %}{{ hidden_initial }} 30 |
31 | {% endpart %} 32 | -------------------------------------------------------------------------------- /demo/templates/material/fields/django_selectdatewidget.html: -------------------------------------------------------------------------------- 1 | {% load material_form material_form_internal %} 2 |
3 |
4 | {% part field label %} 5 |
6 | 7 |
8 | {% endpart %} 9 | {% part field prefix %}{% endpart %} 10 | {% part field control %} 11 | {% with bound_field|select_date_widget_wrapper as wrapper %} 12 | {% for select in wrapper.selects %}{% if select.type == "day" %}
13 | {% for value, choice in select.choices %} 18 | {% endfor %} 19 | 20 |
{% endif %}{% if select.type == "month" %}
21 | {% for value, choice in select.choices %} 26 | {% endfor %} 27 | 28 |
{% endif %}{% if select.type == "year" %}
29 | {% for value, choice in select.choices %} 34 | {% endfor %} 35 | 36 |
{% endif %} 37 | {% endfor %}{% endwith %} 38 |
39 | {% part field help_text %}{% if field.help_text %} 40 |
{{ bound_field.help_text|safe }}
41 | {% endif %}{% endpart %}{% part field errors %} 42 | {% if bound_field.errors %} 43 | {% include 'material/field_errors.html' %} 44 | {% endif %}{% endpart %}{{ hidden_initial }} 45 | {% endpart %} 46 |
47 |
48 |
49 | -------------------------------------------------------------------------------- /demo/templates/material/fields/django_selectmultiple.html: -------------------------------------------------------------------------------- 1 | {% load i18n l10n material_form material_form_internal %} 2 | 3 | {% render bound_field template='fields/django_select.html' %} 4 | {% attr field 'widget' multiple %}True{% endattr %} 5 | {% attr field 'group' class append %}multiselect{% endattr %} 6 | {% part field options %} 7 | {% if not field|have_default_choice %}{% endif %}{% for group, items in bound_field|select_options %}{% if group %}{% endif %}{% for choice, value, selected in items %} 8 | {% endfor %} 9 | {% if group %}{% endif %}{% endfor %} 10 | {% endpart %} 11 | {% endrender %} 12 | -------------------------------------------------------------------------------- /demo/templates/material/fields/django_splitdatetimewidget.html: -------------------------------------------------------------------------------- 1 | {% load i18n material_form material_form_internal %} 2 | {% get_current_language as LANGUAGE_CODE %} 3 | {% get_language_info for LANGUAGE_CODE as lang %} 4 | {% part bound_field.field %}{% part field label %} 5 | {% endpart %} 6 |
7 |
8 | {{ bound_field.label }} 11 |
12 | 16 | {% part field prefix %}{% endpart %}{% part field control %} 17 | view_module 18 | 28 | {% endpart %}{% part field help_text %} 29 | {% part field errors %} 30 | {% if bound_field.errors %} 31 | {% include 'material/field_errors.html' %} 32 | {% endif %} 33 | {% endpart %}{% if field.help_text %} 34 |
{{ bound_field.help_text|safe }}
35 | {% endif %} 36 | {% endpart %}{{ hidden_initial }} 37 |
38 |
40 | query_builder 41 | 51 |
52 | {% endpart %} 53 | -------------------------------------------------------------------------------- /demo/templates/material/fields/django_textarea.html: -------------------------------------------------------------------------------- 1 | {% load material_form material_form_internal %} 2 | {% part bound_field.field %}
3 | 7 | {% part field prefix %}{% endpart %}{% part field control %} 8 | {% if bound_field.value %}{{ bound_field.value }}{% endif %} 13 | {% endpart %} 14 | {% part field label %} 15 | {{ bound_field.label }} 19 | {% endpart %} 20 | {% part field help_text %}{% if field.help_text %} 21 |
{{ bound_field.help_text|safe }}
22 | {% endif %} 23 | {% endpart %}{% part field errors %} 24 | {% if bound_field.errors %} 25 | {% include 'material/field_errors.html' %} 26 | {% endif %} 27 | {% endpart %}{{ hidden_initial }} 28 |
29 | {% endpart %} 30 | -------------------------------------------------------------------------------- /demo/templates/material/fields/django_timeinput.html: -------------------------------------------------------------------------------- 1 | {% load i18n material_form material_form_internal %} 2 | {% get_current_language as LANGUAGE_CODE %} 3 | {% get_language_info for LANGUAGE_CODE as lang %} 4 |
5 | 9 | {% part field prefix %}{% endpart %}{% part field control %} 10 | 21 | {% endpart %} 22 | {% part field label %} 23 | {{ bound_field.label }} 27 | {% endpart %} 28 | {% part field help_text %}{% if field.help_text %} 29 |
{{ bound_field.help_text|safe }}
30 | {% endif %} 31 | {% endpart %}{% part field errors %} 32 | {% if bound_field.errors %} 33 | {% include 'material/field_errors.html' %} 34 | {% endif %}{% endpart %}{{ hidden_initial }} 35 |
36 | 37 | -------------------------------------------------------------------------------- /demo/templates/material/frontend/base_site.html: -------------------------------------------------------------------------------- 1 | {% extends 'material/frontend/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %}vfOA-基于Viewflow的OA演示系统{% endblock %} 5 | 6 | {% block css %} 7 | {{ block.super }} 8 | 9 | 10 | 11 | {% endblock %} 12 | 13 | {% block js %} 14 | {{ block.super }} 15 | 16 | 17 | {% endblock %} 18 | 19 | 20 | {% block topbar_links %} 21 |
  • arrow_drop_downLogin As
  • 22 | 27 | {% endblock %} 28 | 29 | 30 | {% block main %} 31 | {{ block.super }} 32 | 33 | {% include 'demo/wizard.html' %} 34 | {% endblock %} 35 | -------------------------------------------------------------------------------- /demo/testing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/testing/__init__.py -------------------------------------------------------------------------------- /demo/testing/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.utils.translation import ugettext_lazy as _ 3 | # Register your models here. 4 | import django.utils.timezone as timezone 5 | from django.utils.safestring import mark_safe 6 | from django.contrib import admin 7 | from .models import * 8 | from .forms import * 9 | from django.core.urlresolvers import resolve 10 | 11 | # Register your models here. 12 | 13 | 14 | def complete_tasks(modeladmin, request, queryset): 15 | queryset.update(testing_status='1') 16 | complete_tasks.short_description = '测试完成' 17 | 18 | @admin.register(Contract) 19 | class ContractAdmin(admin.ModelAdmin): 20 | 21 | # def get_urls(self): 22 | # pass 23 | 24 | # def get_fields(self, request, obj=None): 25 | # pass 26 | #-----------------********************** 27 | #formfield_for_manytomany 28 | 29 | #------form--------------- 30 | icon = "person" 31 | form = ContractForm 32 | save_as = True 33 | layout = Layout( 34 | Fieldset('检测基本信息', 35 | Row('customer','protocol_no','task_no', 'product_type',), 36 | Row( 'product_num','sample_from', 'sample_to', 'report_get',), 37 | Row('testing_area','report_num',), 38 | Row('testing_class','testing_time_class', 'testing_time',)), 39 | Fieldset('检测依据',Row( 'testing_standards'), Row('testing_items')), 40 | Fieldset('检测费用',Row('testing_fee', 'testing_fee_tax_no','testing_fee_tax_class', )) 41 | ) 42 | 43 | 44 | def _get_object_from_request(self,request): 45 | 46 | resolved = resolve(request.path_info) 47 | if resolved.args: 48 | return Contract.objects.get(pk=resolved.args[0]) 49 | 50 | return None 51 | 52 | def formfield_for_manytomany(self, db_field, request, **kwargs): 53 | 54 | if db_field.name == 'testing_items': 55 | contract = self._get_object_from_request(request) 56 | if contract is not None: 57 | kwargs["queryset"] = TestingItem.objects.filter(testing_type=contract.testing_type) 58 | else: 59 | testin_type_id = request.GET.get('testing-type','1') 60 | one_type = TestingType.objects.filter(id=testin_type_id) 61 | kwargs["queryset"] = TestingItem.objects.filter(testing_type=one_type) 62 | #kwargs.update({"columns":'5'}) 63 | return super(ContractAdmin, self).formfield_for_manytomany(db_field, request, **kwargs) 64 | 65 | 66 | 67 | def save_model(self, request, obj, form, change): 68 | if change: 69 | pass 70 | else: 71 | testin_type_id = request.GET.get('testing-type', '1') 72 | obj.create_datetime = timezone.now() 73 | obj.create_user = request.user 74 | obj.testing_type = TestingType.objects.filter(id=testin_type_id).first() 75 | 76 | super(ContractAdmin, self).save_model(request, obj, form, change) 77 | 78 | 79 | #-------list---------------- 80 | list_display = ('protocol_no', 'customer_name', 'create_datetime', 'user_name','operate') 81 | list_filter = ('customer',) 82 | search_fields = ['protocol_no','customer__name','create_user__username'] 83 | list_display_links = ['protocol_no',] 84 | filter_horizontal = ('testing_standards',) 85 | readonly_fields = ('operate',) 86 | ordering = ('-create_datetime',) 87 | 88 | actions = [complete_tasks,] 89 | 90 | def customer_name(self, contract): 91 | return contract.customer.name 92 | 93 | def user_name(self, contract): 94 | return contract.create_user.username 95 | 96 | 97 | def operate(self, contract): 98 | return mark_safe('修改' 99 | ' | 删除') 100 | 101 | operate.short_description = _('操作') 102 | 103 | 104 | 105 | @admin.register(Customer) 106 | class CustomerAdmin(admin.ModelAdmin): 107 | list_display = ('name', 'address') 108 | 109 | # list_filter = ('req_by','depart_name','req_class') 110 | # search_fields = ('resion',) 111 | 112 | 113 | @admin.register(TestingItem) 114 | class TestingItemAdmin(admin.ModelAdmin): 115 | pass 116 | 117 | @admin.register(TestingType) 118 | class TestingTypeAdmin(admin.ModelAdmin): 119 | pass 120 | 121 | 122 | @admin.register(TestingStandard) 123 | class TestingStandardAdmin(admin.ModelAdmin): 124 | pass 125 | 126 | -------------------------------------------------------------------------------- /demo/testing/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class TestingConfig(AppConfig): 5 | name = 'demo.testing' 6 | -------------------------------------------------------------------------------- /demo/testing/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from material import * 3 | 4 | from . import models 5 | 6 | 7 | 8 | class ContractForm(forms.ModelForm): 9 | 10 | class Meta: 11 | model = models.Contract 12 | fields = ['protocol_no', 13 | 'task_no', 14 | 'product_type', 15 | 'product_num', 16 | 'customer', 17 | 'sample_from', 18 | 'sample_to', 19 | 'report_get', 20 | 'testing_area', 21 | 'report_num', 22 | 'testing_class', 23 | 'testing_time_class', 24 | 'testing_time', 25 | 'testing_standards', 26 | 'testing_items', 27 | 'testing_fee', 28 | 'testing_fee_tax_no', 29 | 'testing_fee_tax_class', 30 | ] 31 | widgets = { 32 | 'testing_items': forms.CheckboxSelectMultiple() 33 | } 34 | 35 | 36 | sample_from = forms.ChoiceField( label='来样方式' , choices=(('1', "送样"),('2', "邮寄"),)) 37 | sample_to = forms.ChoiceField( label='样品处理' ,choices=(('1', "自取"),('2', "放弃"),('3', "邮寄"),('4', "留存"),)) 38 | report_get = forms.ChoiceField( label='报告领取' ,choices=(('1', "自取"),('2', "邮寄"),)) 39 | testing_area = forms.ChoiceField( label='测试地点' ,choices=(('1', "本中心"),('2', "现场"),)) 40 | testing_class = forms.ChoiceField(label='检测类别' ,choices=(('1', "委托"),('2', "型检"),('3', "监督"),('4', "仲裁"),('4', "摸底"),('4', "检查"),)) 41 | testing_time_class = forms.ChoiceField( label='检测时间' ,choices=(('1', "常规"),('2', "加急"),('2', "特急"),)) 42 | testing_fee_tax_class = forms.ChoiceField( label='发票类别' ,choices=(('1', "增值税普通发票"),('2', "增值税专用发票"),)) 43 | #testing_times = forms.MultipleChoiceField(label='测试项目A') 44 | 45 | 46 | # def __init__(self, *args, **kwargs): 47 | # super(ContractCreateForm, self).__init__(*args, **kwargs) 48 | # instance = getattr(self, 'instance', None) 49 | # if instance and instance.id: 50 | # self.fields['testing_items'].widget = forms.CheckboxSelectMultiple -------------------------------------------------------------------------------- /demo/testing/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/demo/testing/migrations/__init__.py -------------------------------------------------------------------------------- /demo/testing/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | 4 | 5 | 6 | class TestingStandard(models.Model): 7 | 8 | 9 | name =models.CharField(max_length=10, verbose_name=u"标准名称") 10 | no = models.CharField(max_length=10, verbose_name=u"协议号") 11 | st_class = models.CharField(max_length=1, verbose_name=u"标准类别",choices=( 12 | ("0", "国标"), 13 | ("1", "行标"), 14 | ("2", "企标"))) 15 | class Meta: 16 | verbose_name = '标准' 17 | verbose_name_plural = "标准" 18 | 19 | 20 | def __str__(self): 21 | stardard_class = {'0': '国标', '1': '行标', '2': '企标'} 22 | return stardard_class[self.st_class] +" | "+ self.name 23 | 24 | class TestingType(models.Model): 25 | name = models.CharField(max_length=1024, verbose_name=u"测试类型") 26 | mark = models.CharField(max_length=1024, verbose_name=u"备注") 27 | 28 | class Meta: 29 | verbose_name = '测试类型' 30 | verbose_name_plural = "测试类型" 31 | 32 | def __str__(self): 33 | return self.name 34 | 35 | class TestingItem(models.Model): 36 | name = models.CharField(max_length=10, verbose_name=u"测试项名称") 37 | #standard = models.ForeignKey(TestingStandard, verbose_name=u"标准类别") 38 | testing_type = models.ForeignKey(TestingType, verbose_name=u"标准类别") 39 | class Meta: 40 | verbose_name = '测试项目' 41 | verbose_name_plural = "测试项目" 42 | 43 | 44 | def __str__(self): 45 | return self.name 46 | 47 | class TestingItemTemp(models.Model): 48 | name = models.CharField(max_length=10, verbose_name=u"模板名称") 49 | items = models.ManyToManyField(TestingItem,verbose_name=u"测试项目") 50 | class Meta: 51 | verbose_name = '测试项目模板' 52 | verbose_name_plural = "测试项目模板" 53 | 54 | 55 | def __str__(self): 56 | return self.name 57 | 58 | 59 | class Customer(models.Model): 60 | name = models.CharField(max_length=1024, verbose_name=u"单位名称") 61 | address = models.CharField(max_length=1024, verbose_name=u"单位地址") 62 | telephone = models.CharField(max_length=1024, verbose_name=u"电话") 63 | atten = models.CharField(max_length=1024, verbose_name=u"联系人") 64 | postcode = models.CharField(max_length=1024, verbose_name=u"邮编") 65 | fax = models.CharField(max_length=1024, verbose_name=u"传真") 66 | 67 | 68 | class Meta: 69 | verbose_name = '委托单位' 70 | verbose_name_plural = "委托单位" 71 | 72 | 73 | def __str__(self): 74 | return self.name 75 | 76 | # Create your models here. 77 | class Contract(models.Model): 78 | protocol_no = models.CharField(max_length=10, verbose_name=u"协议书编号") 79 | task_no = models.CharField(max_length=10, verbose_name=u"任务流水号") 80 | product_type = models.CharField(max_length=1024, verbose_name=u"产品名称/规格型号") 81 | product_num = models.IntegerField( verbose_name=u"数量") 82 | customer = models.ForeignKey(Customer,related_name='contracts',verbose_name=u"委托单位") 83 | sample_from = models.CharField(max_length=1, verbose_name=u"来样方式") 84 | sample_to = models.CharField(max_length=1, verbose_name=u"样品处理") 85 | report_get = models.CharField(max_length=1, verbose_name=u"报告领取") 86 | testing_area = models.CharField(max_length=1, verbose_name=u"测试地点") 87 | report_num = models.IntegerField( verbose_name=u"报告份数",help_text='份数') 88 | testing_class = models.CharField(max_length=1, verbose_name=u"检测类别") 89 | testing_time_class = models.CharField(max_length=1, verbose_name=u"检测时间") 90 | testing_time = models.IntegerField( verbose_name=u"加急",null =True,blank= True,help_text='工作日') 91 | testing_standards = models.ManyToManyField(TestingStandard,verbose_name=u"检测依据") 92 | testing_items = models.ManyToManyField(TestingItem,verbose_name=u"测试项目") 93 | testing_fee = models.FloatField(verbose_name='测试费用',help_text='元',) 94 | testing_fee_tax_no = models.CharField(max_length=1024, verbose_name=u"发票号") 95 | testing_fee_tax_class = models.CharField(max_length=1, verbose_name=u"发票类别") 96 | testing_status = models.CharField(max_length=1, verbose_name=u"检测状态") 97 | testing_type = models.ForeignKey(TestingType, verbose_name=u"标准类别") 98 | create_user = models.ForeignKey(User,verbose_name=u"创建人") 99 | create_datetime = models.DateTimeField( verbose_name=u"创建日期") 100 | 101 | class Meta: 102 | verbose_name = '协议书' 103 | verbose_name_plural = "协议书" 104 | 105 | 106 | def __str__(self): 107 | return self.protocol_no 108 | 109 | -------------------------------------------------------------------------------- /demo/testing/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /demo/testing/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /demo/urls.py: -------------------------------------------------------------------------------- 1 | import django 2 | from django.conf.urls import include, url 3 | from django.contrib import admin 4 | from django.contrib.auth import views as auth 5 | from django.views import generic 6 | 7 | from material import frontend 8 | from material.frontend.apps import ModuleMixin 9 | from material.frontend.registry import modules 10 | 11 | 12 | 13 | class Demo(ModuleMixin): 14 | """ 15 | Home page module 16 | """ 17 | order = 1 18 | label = 'Introduction' 19 | verbose_name = '我的桌面' 20 | icon = 'account_balance' 21 | 22 | @property 23 | def urls(self): 24 | index_view = generic.TemplateView.as_view(template_name='demo/index.html') 25 | 26 | return frontend.ModuleURLResolver( 27 | '^', [url('^$', index_view, name="index")], 28 | module=self, app_name='demo', namespace='demo') 29 | 30 | def index_url(self): 31 | return '/' 32 | 33 | def installed(self): 34 | return True 35 | 36 | modules.register(Demo()) 37 | 38 | 39 | 40 | if django.VERSION < (1, 7): 41 | admin.autodiscover() 42 | 43 | 44 | from material.frontend import urls as frontend_urls # NOQA 45 | 46 | urlpatterns = [ 47 | url(r'^accounts/login/$', auth.login, name='login'), 48 | url(r'^accounts/logout/$', auth.logout, name='logout'), 49 | url(r'^', include('demo.website')), 50 | url(r'', include(frontend_urls)), 51 | ] 52 | -------------------------------------------------------------------------------- /demo/website.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from django.contrib.auth import login, logout 3 | from django.contrib.auth.models import User 4 | from django.http import HttpResponseRedirect 5 | from django.shortcuts import redirect 6 | from django.utils.http import is_safe_url 7 | from .testing.models import TestingType 8 | 9 | 10 | def users(request): 11 | return { 12 | 'users': User.objects.filter(is_active=True).order_by('-username') 13 | } 14 | 15 | def testing_types(request): 16 | return { 17 | 'testing_types':TestingType.objects.all() 18 | } 19 | 20 | def login_as(request): 21 | user = None 22 | 23 | user_pk = request.GET.get('user_pk', None) 24 | if user_pk: 25 | try: 26 | user = User.objects.get(pk=user_pk) 27 | except User.DoesNotExist: 28 | pass 29 | 30 | username = request.GET.get('username', None) 31 | if username: 32 | try: 33 | user = User.objects.get(username=username) 34 | except User.DoesNotExist: 35 | pass 36 | 37 | if user: 38 | user.backend = 'django.contrib.auth.backends.ModelBackend' 39 | login(request, user) 40 | else: 41 | logout(request) 42 | 43 | redirect_to = request.GET.get('next') 44 | if not redirect_to or not is_safe_url(redirect_to): 45 | return redirect('/admin/testing/contract') 46 | else: 47 | return HttpResponseRedirect(redirect_to) 48 | 49 | 50 | urlpatterns = [ 51 | url(r'^login_as/$', login_as, name="login_as") 52 | ] 53 | -------------------------------------------------------------------------------- /img/1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/img/1.PNG -------------------------------------------------------------------------------- /img/2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/img/2.PNG -------------------------------------------------------------------------------- /img/3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/img/3.PNG -------------------------------------------------------------------------------- /img/4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/img/4.PNG -------------------------------------------------------------------------------- /img/5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/htwenhe/vfOA/4746a894d0827a48bb42a79e6a8a905dd0d60b65/img/5.PNG -------------------------------------------------------------------------------- /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", "demo.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | django==1.11.7 2 | django-filter==1.1.0 3 | django-formtools==2.1 4 | django-material==1.1.1 5 | django-viewflow==1.1.0 6 | --------------------------------------------------------------------------------