├── SQL └── mis.sql ├── basedata ├── __init__.py ├── admin.py ├── apps.py ├── models.py ├── tests.py ├── urls.py └── views.py ├── common ├── __init__.py ├── const.py └── generic.py ├── hr ├── __init__.py ├── admin.py ├── apps.py ├── models.py ├── tests.py └── views.py ├── invent ├── __init__.py ├── admin.py ├── apps.py ├── models.py ├── tests.py ├── urls.py └── views.py ├── locale └── zh_CN │ └── LC_MESSAGES │ ├── django.mo │ └── django.po ├── manage.py ├── midware ├── __init__.py └── cuser.py ├── mis ├── __init__.py ├── production.py ├── settings.py ├── urls.py ├── views.py └── wsgi.py ├── organ ├── __init__.py ├── admin.py ├── apps.py ├── models.py ├── tests.py └── views.py ├── plugin ├── __init__.py ├── wfactions.py ├── wfnodes.py ├── wfusers.py └── xls.py ├── purchase ├── __init__.py ├── admin.py ├── apps.py ├── models.py ├── tests.py └── views.py ├── sale ├── __init__.py ├── admin.py ├── apps.py ├── models.py ├── tests.py └── views.py ├── selfhelp ├── __init__.py ├── admin.py ├── apps.py ├── models.py ├── tests.py ├── urls.py └── views.py ├── static ├── css │ └── maximus.css ├── img │ ├── icon-submitlink.gif │ ├── icon-submitlink.png │ └── icon-yes.gif └── js │ ├── maximus.js │ └── workorder.js ├── syscfg ├── __init__.py ├── admin.py ├── apps.py ├── models.py ├── tests.py └── views.py ├── templates ├── admin │ ├── app_index.html │ ├── base.html │ ├── change_form.html │ ├── index.html │ ├── invent │ │ ├── inventory │ │ │ └── change_form.html │ │ ├── stockin │ │ │ ├── change_form.html │ │ │ └── in_confirmation.html │ │ └── stockout │ │ │ ├── change_form.html │ │ │ └── out_confirmation.html │ ├── object_history.html │ ├── selfhelp │ │ └── workorder │ │ │ └── change_form.html │ ├── submit_line.html │ └── workflow │ │ └── node │ │ └── change_form.html └── default │ └── workflow │ ├── workflow_approve_confirmation.html │ └── workflow_start_confirmation.html ├── upload ├── data │ ├── 1001部门岗位员工导入模板test.xls │ ├── 1003合作伙伴导入模板.xls │ ├── 1003合作伙伴导入模板_ENnUPsg.xls │ ├── 1004项目导入模板.xls │ ├── 1004项目导入模板_MDj3PCr.xls │ ├── 1004项目导入模板_QE5NXrr.xls │ ├── 1004项目导入模板_Sfco4Ck.xls │ ├── 1004项目导入模板_YfVeRXU.xls │ ├── 1005用户信息导入模板.xls │ ├── 1005用户信息导入模板_LRE2NKU.xls │ ├── 1005用户信息导入模板_stkXmtR.xls │ └── 合作伙伴.xls ├── doc │ ├── 1001部门岗位员工导入模板.xls │ ├── 1002期初库存导入模板.csv │ ├── 1003合作伙伴导入模板.xls │ ├── 1004项目导入模板.xls │ ├── 2001报价单明细样例.xls │ ├── 2002需求计划明细样例.xls │ └── 2003采购单明细样例.xls ├── inventory │ ├── INVENTORY.csv │ └── INVENTORY_PPfDioe.csv ├── offer sheet │ └── backbone.js └── project │ ├── 2014年17月份时事政治全集.docx │ └── 综合知识与能力素质.docx ├── workflow ├── __init__.py ├── admin.py ├── apps.py ├── models.py ├── tests.py └── views.py └── 文档 ├── 00产品资料-功能表.doc ├── 10使用手册-功能概述.doc ├── 20使用手册-基本操作.doc ├── 30使用手册-个人自助.doc ├── 40使用手册-销售管理.doc ├── 50使用手册-采购管理.doc ├── 60使用手册-库存管理.doc └── 70使用手册-工作流管理.doc /basedata/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | default_app_config = 'basedata.apps.BaseDataConfig' -------------------------------------------------------------------------------- /basedata/apps.py: -------------------------------------------------------------------------------- 1 | __author__ = 'zhugl' 2 | # created at 15-4-22 3 | from django.apps.config import AppConfig 4 | from django.utils.translation import ugettext_lazy as _ 5 | 6 | 7 | class BaseDataConfig(AppConfig): 8 | name = 'basedata' 9 | verbose_name = _('BaseData') -------------------------------------------------------------------------------- /basedata/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /basedata/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import include, url,static 2 | import basedata.views 3 | 4 | urlpatterns = [ 5 | url(r"dataimport/(?P\d+)/action", 'basedata.views.action_import'), 6 | ] 7 | -------------------------------------------------------------------------------- /basedata/views.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from django.contrib.admin import site 3 | from django.http.response import HttpResponseRedirect 4 | from django.utils.encoding import force_text 5 | from django.template.response import TemplateResponse 6 | from django.contrib import messages 7 | from basedata.models import DataImport 8 | from django.utils.translation import ugettext_lazy as _ 9 | 10 | 11 | def action_import(request,object_id): 12 | """ 13 | 数据导入操作 14 | :param request: 15 | :param object_id: 16 | :return: 17 | """ 18 | title = _("Are you sure?") 19 | obj = DataImport.objects.get(id=int(object_id)) 20 | opts = obj._meta 21 | objects_name = force_text(opts.verbose_name) 22 | 23 | if request.POST.get("post"): 24 | obj.action_import(request) 25 | try: 26 | 27 | messages.success(request,_('data import successfully')) 28 | except Exception,e: 29 | messages.error(request,e) 30 | 31 | return HttpResponseRedirect("/admin/basedata/dataimport/%s"%(object_id)) 32 | 33 | context = dict( 34 | site.each_context(request), 35 | title=title, 36 | opts=opts, 37 | objects_name=objects_name, 38 | object=obj, 39 | ) 40 | request.current_app = site.name 41 | 42 | return TemplateResponse(request,'admin/invent/stockin/in_confirmation.html', context) 43 | -------------------------------------------------------------------------------- /common/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'Administrator' 2 | -------------------------------------------------------------------------------- /common/const.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from django.db import connection 3 | from django.utils.translation import ugettext_lazy as _ 4 | __author__ = 'zhugl' 5 | 6 | DB_CHAR_CODE_2 = 2 7 | DB_CHAR_CODE_4 = 4 8 | DB_CHAR_CODE_6 = 6 9 | DB_CHAR_CODE_8 = 8 10 | DB_CHAR_CODE_10 = 10 11 | 12 | DB_CHAR_NAME_20 = 20 13 | DB_CHAR_NAME_40 = 40 14 | DB_CHAR_NAME_60 = 60 15 | DB_CHAR_NAME_80 = 80 16 | DB_CHAR_NAME_120 = 120 17 | DB_CHAR_NAME_200 = 200 18 | 19 | 20 | STATUS_ON_OFF = ( 21 | (0,_('OFF')), 22 | (0,_('ON')), 23 | ) 24 | 25 | 26 | def get_value_list(group): 27 | """ 28 | 获取值列表信息 29 | """ 30 | if group: 31 | try: 32 | cursor = connection.cursor() 33 | cursor.execute('SELECT code,name FROM basedata_valuelistitem WHERE group_code=%s AND status=1',[group]) 34 | rows = cursor.fetchall() 35 | return tuple([(code,name) for code,name in rows]) 36 | except Exception,e: 37 | return None 38 | else: 39 | return None 40 | -------------------------------------------------------------------------------- /common/generic.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | __author__ = 'zhugl' 3 | # created at 15-4-21 4 | import datetime 5 | import xlwt 6 | import re 7 | from django.db import models 8 | from django.db import connection,transaction 9 | from django.db.models import fields 10 | from django.db.models.fields import related 11 | from django.contrib import admin 12 | from django.http import HttpRequest,HttpResponseRedirect,HttpResponse 13 | from django.contrib.contenttypes.models import ContentType 14 | from django.utils.text import force_text 15 | from django.utils.encoding import smart_str 16 | from django.utils.http import urlquote 17 | from django.utils.translation import ugettext_lazy as _ 18 | from common import const 19 | from midware import cuser 20 | 21 | 22 | def update(sql, params=None): 23 | """ 24 | :param sql: 25 | :param params: 26 | :return: 27 | """ 28 | cursor = connection.cursor() 29 | with transaction.atomic(): 30 | try: 31 | if params: 32 | cursor.execute(sql,params) 33 | else: 34 | cursor.execute(sql) 35 | except Exception,e: 36 | print e 37 | 38 | def get_app_model_info_from_request(request): 39 | """ 40 | 41 | """ 42 | if request and isinstance(request,HttpRequest): 43 | import re 44 | pattern = re.compile(r"/(admin)/(\w+)/(\w+)/(\d+)") 45 | match = pattern.match(request.path) 46 | 47 | if match and match.group(): 48 | app = match.group(2) 49 | model = match.group(3) 50 | oid = match.group(4) 51 | ct = ContentType.objects.get(app_label=app,model=model) 52 | obj = ct.get_object_for_this_type(id=oid) 53 | return {'app':app,'model':model,'id':oid,'obj':obj} 54 | return None 55 | 56 | 57 | class MineBOManager(models.Manager): 58 | """ 59 | get the objects created by current login user 60 | """ 61 | def get_query_set(self): 62 | return super(MineBOManager,self).get_query_set().filter(creator=cuser.getuser()) 63 | 64 | 65 | class BOManager(models.Manager): 66 | """ 67 | """ 68 | def get_query_set(self): 69 | return super(BOManager,self).get_query_set() 70 | 71 | 72 | class BO(models.Model): 73 | """ 74 | All business object derive from this class 75 | """ 76 | begin = models.DateField(_('begin date'),blank=True,null=True) 77 | end = models.DateField(_('end date'),blank=True,null=True) 78 | creator = models.CharField(_("creator"),blank=True,null=True,max_length=const.DB_CHAR_NAME_20) 79 | modifier = models.CharField(_("modifier"),blank=True,null=True,max_length=const.DB_CHAR_NAME_20) 80 | creation = models.DateTimeField(_('creation'),auto_now_add=True,blank=True,null=True) 81 | modification = models.DateTimeField(_('modification'),auto_now=True,blank=True,null=True) 82 | # mine = MineBOManager() 83 | objects = models.Manager() 84 | 85 | def __unicode__(self): 86 | display = getattr(self,'name',None) or getattr(self,'title',None) or getattr(self,'description',None) 87 | if not display: 88 | display = ' ' 89 | return u'%s' % display 90 | 91 | class Meta: 92 | abstract = True 93 | 94 | 95 | class BOAdmin(admin.ModelAdmin): 96 | """ 97 | All business object admin derive from this class 98 | """ 99 | CODE_NUMBER_WIDTH = 4 100 | CODE_PREFIX = '9' 101 | extra_buttons = [] 102 | 103 | exclude = ['creator','modifier','creation','modification','begin','end'] 104 | list_per_page = 18 105 | actions = ['export_selected_data'] 106 | 107 | def changeform_view(self, request, object_id=None, form_url='', extra_context=None): 108 | """ 109 | 110 | :param request: 111 | :param object_id: 112 | :param form_url: 113 | :param extra_context: 114 | :return: 115 | """ 116 | app_info = get_app_model_info_from_request(request) 117 | workflow_modal = None 118 | workflow_instance = None 119 | has_workflow_modal = False 120 | has_workflow_instance = False 121 | show_workflow_line = False 122 | show_submit_button = False 123 | can_restart = False 124 | can_edit = False 125 | # print app_info 126 | if app_info: 127 | try: 128 | modal = ContentType.objects.get(app_label='workflow',model='modal') 129 | workflow_modal = modal.get_object_for_this_type(app_name=app_info['app'],model_name=app_info['model']) 130 | has_workflow_modal = True 131 | # print workflow_modal.code 132 | instance = ContentType.objects.get(app_label='workflow',model='instance') 133 | workflow_instance = instance.get_object_for_this_type(modal=workflow_modal,object_id=app_info['id']) 134 | has_workflow_instance = True 135 | todo = ContentType.objects.get(app_label='workflow',model='todolist') 136 | todo_list = todo.model_class().objects.filter(inst=workflow_instance,status=0,user=request.user) 137 | x = todo_list.all() 138 | 139 | if x and len(x)>0: 140 | can_edit = x[0].node.can_edit 141 | if todo_list.count() > 0: 142 | # print 'we fount it' 143 | unread = todo_list.filter(is_read=0) 144 | show_workflow_line = True 145 | if unread.count() > 0: 146 | unread.update(is_read=1,read_time=datetime.datetime.now()) 147 | if workflow_instance.status == 3 and request.user == workflow_instance.starter: 148 | can_restart = True 149 | show_workflow_line = True 150 | 151 | except Exception,e: 152 | print Exception,e 153 | 154 | if workflow_modal and not workflow_instance: 155 | show_submit_button = True 156 | extra_context = extra_context or {} 157 | ctx = dict( 158 | has_workflow_instance = has_workflow_instance, 159 | has_workflow_modal = has_workflow_modal, 160 | workflow_modal = workflow_modal, 161 | workflow_instance = workflow_instance, 162 | show_workflow_line = show_workflow_line, 163 | can_restart = can_restart, 164 | can_edit = can_edit, 165 | show_submit_button = show_submit_button, 166 | ) 167 | if len(self.extra_buttons) > 0: 168 | buttons = dict( 169 | extra_buttons = self.extra_buttons 170 | ) 171 | ctx.update(buttons) 172 | extra_context.update(ctx) 173 | # print extra_context 174 | return super(BOAdmin,self).changeform_view(request,object_id,form_url,extra_context) 175 | 176 | def history_view(self, request, object_id, extra_context=None): 177 | """ 178 | 179 | :param request: 180 | :param object_id: 181 | :param extra_context: 182 | :return: 183 | """ 184 | app_info = get_app_model_info_from_request(request) 185 | # print app_info 186 | if app_info: 187 | try: 188 | modal = ContentType.objects.get(app_label='workflow',model='modal') 189 | workflow_modal = modal.get_object_for_this_type(app_name=app_info['app'],model_name=app_info['model']) 190 | has_workflow_modal = True 191 | instance = ContentType.objects.get(app_label='workflow',model='instance') 192 | workflow_instance = instance.get_object_for_this_type(modal=workflow_modal,object_id=app_info['id']) 193 | has_workflow_instance = True 194 | history = ContentType.objects.get(app_label='workflow',model='history') 195 | history_list = history.model_class().objects.filter(inst=workflow_instance) 196 | has_history = True 197 | todo = ContentType.objects.get(app_label='workflow',model='todolist') 198 | todo_list = todo.model_class().objects.filter(inst=workflow_instance,status=0).exclude(node=None) 199 | 200 | extra_context = extra_context or {} 201 | ctx = dict( 202 | has_workflow_instance = has_workflow_instance, 203 | has_workflow_modal = has_workflow_modal, 204 | workflow_modal = workflow_modal, 205 | workflow_instance = workflow_instance, 206 | history_list = history_list, 207 | has_history = has_history, 208 | todo_list = todo_list, 209 | ) 210 | # print history_list 211 | extra_context.update(ctx) 212 | except Exception,e: 213 | print Exception,e 214 | pass 215 | return super(BOAdmin,self).history_view(request,object_id,extra_context) 216 | 217 | def get_changeform_initial_data(self, request): 218 | import datetime 219 | return {'begin':datetime.date.today,'end':datetime.date(9999,12,31)} 220 | 221 | def save_model(self, request, obj, form, change): 222 | 223 | if change: 224 | setattr(obj,'modifier',request.user.username) 225 | else: 226 | setattr(obj,'creator',request.user.username) 227 | setattr(obj,'begin',datetime.date.today()) 228 | setattr(obj,'end',datetime.date(9999,12,31)) 229 | try: 230 | setattr(obj,'user',request.user) 231 | except Exception,e: 232 | pass 233 | 234 | super(BOAdmin,self).save_model(request,obj,form,change) 235 | # print '=========it is here=========' 236 | try: 237 | code = getattr(obj,'code') 238 | # print code 239 | if code is None or len(code) == 0: 240 | fmt = '%s%0'+str(self.CODE_NUMBER_WIDTH)+'d' 241 | code = fmt % (self.CODE_PREFIX,obj.id) 242 | table = obj._meta.db_table 243 | sql = 'update %s set code = \'%s\' where id=%s' % (table,code,obj.id) 244 | print sql 245 | update(sql) 246 | except Exception,e: 247 | print e 248 | 249 | # def response_change(self, request, obj): 250 | # return HttpResponseRedirect('') 251 | 252 | def export_selected_data(self,request,queryset): 253 | ops = self.model._meta 254 | workbook = xlwt.Workbook(encoding='utf-8') 255 | dd = datetime.date.today().strftime('%Y%m%d') 256 | file_name = force_text(ops.verbose_name+dd) 257 | sheet = workbook.add_sheet(force_text(ops.verbose_name)) 258 | obj_fields = getattr(self,'export_fields',None) or self.list_display or self.fields 259 | 260 | head_col_index = 0 261 | for field in obj_fields: 262 | col_name = field 263 | try: 264 | f = ops.get_field(field) 265 | col_name = f.verbose_name 266 | except Exception,e: 267 | f = getattr(self.model,field) 268 | if hasattr(f,'short_description'): 269 | col_name = f.short_description 270 | sheet.write(0,head_col_index,force_text(col_name)) 271 | head_col_index+=1 272 | row_index = 1 273 | for obj in queryset: 274 | col_index = 0 275 | for field in obj_fields: 276 | f = field 277 | try: 278 | f = ops.get_field(field) 279 | except Exception,e: 280 | pass 281 | v = getattr(obj,field,'') 282 | if hasattr(v,'__call__') or callable(v): 283 | v = v() 284 | elif type(f) == fields.DateField: 285 | v = v.strftime('%Y-%m-%d') 286 | elif type(f) == fields.DateTimeField: 287 | v = v.strftime('%Y-%m-%d %H:%M') 288 | elif type(f) == fields.CharField and f.choices: 289 | fc = 'get_'+field+'_display' 290 | v = getattr(obj,fc)() 291 | elif type(f) == related.ForeignKey: 292 | v = str(v) 293 | sheet.write(row_index,col_index,v) 294 | col_index += 1 295 | row_index += 1 296 | response = HttpResponse(content_type='application/vnd.ms-excel') 297 | agent = request.META.get('HTTP_USER_AGENT') 298 | nn = smart_str(file_name) 299 | if agent and re.search('MSIE',agent): 300 | nn = urlquote(file_name) 301 | response['Content-Disposition'] = 'attachment; filename=%s.xls'%nn 302 | workbook.save(response) 303 | return response 304 | #self.message_user(request,'SUCCESS') 305 | export_selected_data.short_description = _("export selected %(verbose_name_plural)s") 306 | 307 | class Meta: 308 | ordering = ['-creation'] 309 | 310 | class Media: 311 | css = {'all':('css/maximus.css',)} 312 | js = ('js/maximus.js',) 313 | -------------------------------------------------------------------------------- /hr/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'hr.apps.MyAppConfig' -------------------------------------------------------------------------------- /hr/admin.py: -------------------------------------------------------------------------------- 1 | # coding = utf-8 2 | from django.contrib import admin 3 | from django.utils.translation import ugettext_lazy as _ 4 | from common import generic 5 | from common import const 6 | from hr.models import Entry,SalaryItem,EmployeeSalaryItem 7 | 8 | 9 | class SalaryItemAdmin(admin.ModelAdmin): 10 | list_display = ['code','classification','name','plus_or_minus','required'] 11 | list_display_links = ['code','name'] 12 | list_per_page = 20 13 | 14 | 15 | class EmployeeSalaryItemInline(admin.TabularInline): 16 | model = EmployeeSalaryItem 17 | exclude = ['employee'] 18 | 19 | def formfield_for_foreignkey(self, db_field, request=None, **kwargs): 20 | if db_field.name == 'salary_item': 21 | kwargs['queryset'] = SalaryItem.objects.filter(required=1) 22 | 23 | return super(EmployeeSalaryItemInline,self).formfield_for_foreignkey(db_field,request,**kwargs) 24 | 25 | def get_extra(self, request, obj=None, **kwargs): 26 | if obj: 27 | return 0 28 | else: 29 | return 3 30 | 31 | 32 | class EntryAdmin(generic.BOAdmin): 33 | list_display = ['code','name','gender','position','rank','probation_months','probation_end'] 34 | inlines = [EmployeeSalaryItemInline] 35 | fieldsets = [ 36 | (None,{'fields':[('code','position',),('name','pinyin',),('address','zipcode',),('idcard','phone',),('memo',),('profile',)]}), 37 | (_('org distribute'),{'fields':[('guider',),('rank','ygxs',),('category','probation_months',),('probation_begin','probation_end',)],'classes': ['collapse']}) 38 | ] 39 | raw_id_fields = ['position','guider'] 40 | def get_changeform_initial_data(self, request): 41 | import datetime 42 | end = datetime.date.today()+datetime.timedelta(90) 43 | return {'probation_end':end} 44 | 45 | 46 | admin.site.register(SalaryItem,SalaryItemAdmin) 47 | admin.site.register(Entry,EntryAdmin) -------------------------------------------------------------------------------- /hr/apps.py: -------------------------------------------------------------------------------- 1 | # created at 15-5-23 2 | # coding=utf-8 3 | __author__ = 'zhugl' 4 | 5 | from django.apps import AppConfig 6 | from django.utils.translation import ugettext_lazy as _ 7 | 8 | 9 | class MyAppConfig(AppConfig): 10 | name = 'hr' 11 | verbose_name = _("human resource") 12 | -------------------------------------------------------------------------------- /hr/models.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from django.db import models 3 | from django.contrib.auth.models import User 4 | from django.utils.translation import ugettext_lazy as _ 5 | from common import generic 6 | from common import const 7 | from basedata.models import Position,Employee 8 | from organ.models import OrgUnit 9 | import datetime 10 | 11 | 12 | class SalaryItemHandler: 13 | code = None 14 | 15 | def __init__(self,employee): 16 | self.employee = employee 17 | 18 | def value(self): 19 | return 0 20 | 21 | 22 | class SalaryItem(models.Model): 23 | """ 24 | 工资项 25 | """ 26 | formulas = {} 27 | 28 | @classmethod 29 | def add_formula(cls, code, handler): 30 | SalaryItem.formulas[code] = handler 31 | 32 | @classmethod 33 | def get_formula(cls): 34 | return SalaryItem.formulas.get(cls.code,None) 35 | 36 | code = models.CharField(_("code"),max_length=const.DB_CHAR_CODE_10,null=True) 37 | name = models.CharField(_("name"),max_length=const.DB_CHAR_NAME_120) 38 | classification = models.CharField(_("classification"),max_length=const.DB_CHAR_CODE_2,choices=const.get_value_list('S048'),default='10') 39 | plus_or_minus = models.CharField(_("plus or minus"),max_length=const.DB_CHAR_CODE_2,choices=const.get_value_list('S049'),default='+') 40 | required = models.BooleanField(_("is required"),default=0) 41 | 42 | def __unicode__(self): 43 | return "%s %s" % (self.code,self.name) 44 | 45 | class Meta: 46 | verbose_name = _('salary item') 47 | verbose_name_plural = _('salary items') 48 | ordering = ('code',) 49 | 50 | 51 | class Entry(generic.BO): 52 | """ 53 | 人员入职 54 | """ 55 | code = models.CharField(_("employee number"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True) 56 | name = models.CharField(_("employee name"),max_length=const.DB_CHAR_NAME_120) 57 | pinyin = models.CharField(_("pinyin"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True) 58 | birthday = models.DateField(_("birthday"),blank=True,null=True) 59 | gender = models.CharField(_("gender"),max_length=const.DB_CHAR_CODE_2,choices=const.get_value_list('gender'),default='1') 60 | idcard = models.CharField(_("id card"),max_length=const.DB_CHAR_NAME_20) 61 | address = models.CharField(_("mail address"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True) 62 | zipcode = models.CharField(_("zipcode"),max_length=const.DB_CHAR_CODE_8) 63 | phone = models.CharField(_("phone"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True) 64 | 65 | guider = models.ForeignKey(Employee,verbose_name=_("guider")) 66 | position = models.ForeignKey(Position,verbose_name = _('designate position')) 67 | rank = models.CharField(_("employee rank"),max_length=const.DB_CHAR_CODE_2,default='00',choices=const.get_value_list('S017')) 68 | ygxs = models.CharField(_("employ ygxs"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S019'),default='2') 69 | category = models.CharField(_("employ category"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S018'),default='21') 70 | 71 | probation_months = models.CharField(_("probation months"),max_length=2,default='3',choices=const.get_value_list('S047')) 72 | probation_begin = models.DateField(_("probation begin"),default=datetime.date.today) 73 | probation_end = models.DateField(_("probation end"),blank=True,null=True) 74 | 75 | memo = models.TextField(_("memo"),blank=True,null=True) 76 | profile = models.FileField(_("profile"),blank=True,null=True,upload_to='hr profile') 77 | 78 | class Meta: 79 | verbose_name = _("employee entry") 80 | verbose_name_plural = _("employee entries") 81 | permissions = ( 82 | ('modify_salary_item',_("modify salary item")), 83 | ) 84 | 85 | 86 | class EmployeeSalaryItem(models.Model): 87 | """ 88 | 89 | """ 90 | entry = models.ForeignKey(Entry,verbose_name=_("employee entry")) 91 | employee = models.ForeignKey(Employee,verbose_name=_("employee"),blank=True,null=True) 92 | salary_item = models.ForeignKey(SalaryItem,verbose_name=_("salary item")) 93 | calculate_way = models.CharField(_("calculate way"),max_length=const.DB_CHAR_CODE_2,choices=const.get_value_list('S050'),default='10') 94 | fixed_value = models.DecimalField(_("fixed value"),blank=True,null=True,max_digits=10,decimal_places=2) 95 | base_value = models.DecimalField(_("base value"),blank=True,null=True,max_digits=10,decimal_places=2) 96 | org_percent = models.DecimalField(_("org percent"),blank=True,null=True,max_digits=4,decimal_places=2) 97 | employee_percent = models.DecimalField(_("employee percent"),blank=True,null=True,max_digits=4,decimal_places=2) 98 | 99 | class Meta: 100 | verbose_name = _("salary item") 101 | verbose_name_plural = _("salary item") 102 | unique_together = (('entry', 'salary_item'),) 103 | 104 | 105 | class Transfer(generic.BO): 106 | """ 107 | 人员调动 108 | """ 109 | employee = models.ForeignKey(Employee,verbose_name=_("employee")) 110 | 111 | class Meta: 112 | verbose_name = _("employee transfer") 113 | verbose_name_plural = _("employee transfers") 114 | 115 | 116 | class Departure(generic.BO): 117 | """ 118 | 人员离职 119 | """ 120 | employee = models.ForeignKey(Employee,verbose_name=_("employee")) 121 | 122 | class Meta: 123 | verbose_name = _("employee departure") 124 | verbose_name_plural = _("employee departures") 125 | 126 | -------------------------------------------------------------------------------- /hr/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /hr/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /invent/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = "invent.apps.MyAppConfig" -------------------------------------------------------------------------------- /invent/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.utils.translation import ugettext_lazy as _ 3 | from common import generic 4 | from basedata.models import Material 5 | from invent.models import StockIn,StockOut,InitialInventory,InItem,OutItem,InitItem,Inventory,InItemForm,InOutDetail,\ 6 | WareReturn,ReturnItem,WareAdjust,AdjustItem 7 | 8 | 9 | class InItemInline(admin.TabularInline): 10 | model = InItem 11 | form = InItemForm 12 | fields = ('material', 'measure', 'cnt', 'price') 13 | raw_id_fields = ['material'] 14 | 15 | def get_extra(self, request, obj=None, **kwargs): 16 | if obj: 17 | return 0 18 | else: 19 | return 1 20 | 21 | def get_readonly_fields(self, request, obj=None): 22 | if obj and obj.status == 1: 23 | return ['material', 'measure', 'cnt', 'price'] 24 | else: 25 | return [] 26 | 27 | def formfield_for_foreignkey(self, db_field, request=None, **kwargs): 28 | if db_field.name == 'material': 29 | kwargs['queryset'] = Material.objects.filter(is_virtual=0) 30 | return super(InItemInline,self).formfield_for_foreignkey(db_field,request,**kwargs) 31 | 32 | 33 | class OutItemInline(admin.TabularInline): 34 | model = OutItem 35 | fields = ('inventory', 'measure', 'cnt', 'price','warehouse',) 36 | raw_id_fields = ['inventory'] 37 | readonly_fields = ['measure', 'price', 'warehouse'] 38 | 39 | def get_extra(self, request, obj=None, **kwargs): 40 | if obj: 41 | return 0 42 | else: 43 | return 3 44 | 45 | 46 | class InitItemInline(admin.TabularInline): 47 | model = InitItem 48 | fields = ('material', 'measure', 'cnt', 'warehouse', 'price',) 49 | raw_id_fields = ['material'] 50 | 51 | def get_readonly_fields(self, request, obj=None): 52 | if obj and obj.execute_time: 53 | return ['material', 'measure', 'cnt', 'warehouse', 'price'] 54 | else: 55 | return [] 56 | 57 | def get_extra(self, request, obj=None, **kwargs): 58 | if obj: 59 | return 0 60 | else: 61 | return 3 62 | 63 | def formfield_for_foreignkey(self, db_field, request=None, **kwargs): 64 | if db_field.name == 'material': 65 | kwargs['queryset'] = Material.objects.filter(is_virtual=0) 66 | return super(InitItemInline,self).formfield_for_foreignkey(db_field,request,**kwargs) 67 | 68 | 69 | class StockInAdmin(generic.BOAdmin): 70 | CODE_PREFIX = 'RK' 71 | CODE_NUMBER_WIDTH = 5 72 | list_display = ['code','title','money_of_amount','status','entry_time'] 73 | inlines = [InItemInline] 74 | raw_id_fields = ['po'] 75 | fields = ( 76 | ('code',),('title',),('po',),('warehouse',),('batch',),('status','amount',) 77 | ) 78 | date_hierarchy = 'begin' 79 | extra_buttons = [{'href':'cin','title':_('Action Stock In')}] 80 | 81 | def save_model(self, request, obj, form, change): 82 | import decimal 83 | super(StockInAdmin,self).save_model(request,obj,form,change) 84 | if obj and obj.po: 85 | po_items = obj.po.poitem_set.filter(left_cnt__gt=0).all() 86 | for item in po_items: 87 | try: 88 | InItem.objects.get(po_item=item,master=obj) 89 | continue 90 | except Exception,e: 91 | pp = item.discount_price or item.price 92 | if decimal.Decimal(item.tax) > decimal.Decimal(0): 93 | pp = pp /(decimal.Decimal(1)+decimal.Decimal(item.tax)) 94 | InItem.objects.create(warehouse=obj.warehouse,material=item.material,measure=item.measure,prop='+', 95 | po_item=item,master=obj,cnt=item.left_cnt,price=pp,batch=obj.batch,source=obj.code) 96 | 97 | def get_readonly_fields(self, request, obj=None): 98 | print obj 99 | if obj and obj.status == 9: 100 | return ['code','title','po','warehouse','batch','status'] 101 | else: 102 | return ['status','amount'] 103 | 104 | def changeform_view(self, request, object_id=None, form_url='', extra_context=None): 105 | extra_context = extra_context or {} 106 | if object_id: 107 | obj = StockIn.objects.get(id=object_id) 108 | if obj and obj.execute_time: 109 | extra_context.update(dict(readonly=True)) 110 | return super(StockInAdmin,self).changeform_view(request,object_id,form_url,extra_context) 111 | 112 | 113 | class StockOutAdmin(generic.BOAdmin): 114 | CODE_PREFIX = 'CK' 115 | CODE_NUMBER_WIDTH = 5 116 | list_display = ['code','title','project','status','out_time','out_amount'] 117 | list_display_links = ['code','title'] 118 | date_hierarchy = 'begin' 119 | inlines = [OutItemInline] 120 | raw_id_fields = ['project','wo','user'] 121 | fields = ( 122 | ('code', 'status',),('project', ),('wo','user'), 123 | ('title','amount',),('description',), 124 | ) 125 | readonly_fields = ['status'] 126 | extra_buttons = [{'href':'out','title':_('Action Stock Out')}] 127 | search_fields = ['code','title','user__username'] 128 | 129 | def changeform_view(self, request, object_id=None, form_url='', extra_context=None): 130 | extra_context = extra_context or {} 131 | if object_id: 132 | obj = StockOut.objects.get(id=object_id) 133 | if obj and obj.execute_time: 134 | extra_context.update(dict(readonly=True)) 135 | return super(StockOutAdmin,self).changeform_view(request,object_id,form_url,extra_context) 136 | 137 | def save_model(self, request, obj, form, change): 138 | if obj and obj.user is None: 139 | obj.user = request.user 140 | super(StockOutAdmin,self).save_model(request,obj,form,change) 141 | 142 | 143 | class InitialInventoryAdmin(generic.BOAdmin): 144 | CODE_PREFIX = 'QC' 145 | CODE_NUMBER_WIDTH = 3 146 | list_display = ['code','title','status'] 147 | inlines = [InitItemInline] 148 | fields = ('code','title','org','status','amount','attach') 149 | readonly_fields = ['status','amount'] 150 | extra_buttons = [{'href':'cin','title':_('Action Stock In')}] 151 | 152 | def changeform_view(self, request, object_id=None, form_url='', extra_context=None): 153 | extra_context = extra_context or {} 154 | if object_id: 155 | obj = InitialInventory.objects.get(id=object_id) 156 | if obj and obj.execute_time: 157 | extra_context.update(dict(readonly=True)) 158 | return super(InitialInventoryAdmin,self).changeform_view(request,object_id,form_url,extra_context) 159 | 160 | 161 | class InventoryAdmin(generic.BOAdmin): 162 | list_display = ['material','measure','warehouse','cnt','price'] 163 | search_fields = ['material__name','material__code'] 164 | 165 | def changeform_view(self, request, object_id=None, form_url='', extra_context=None): 166 | extra_context = extra_context or {} 167 | extra_context.update(dict(readonly=True)) 168 | if object_id: 169 | inventory = Inventory.objects.get(id=object_id) 170 | material = inventory.material 171 | warehouse = inventory.warehouse 172 | detail = InOutDetail.objects.filter(material=material,warehouse=warehouse) 173 | extra_context.update(dict(detail=detail)) 174 | 175 | return super(InventoryAdmin,self).changeform_view(request,object_id,form_url,extra_context) 176 | 177 | 178 | class ReturnItemInline(admin.TabularInline): 179 | model = ReturnItem 180 | fields = ['material','measure','warehouse','out_cnt','cnt'] 181 | readonly_fields = ['material','measure','warehouse','out_cnt'] 182 | extra = 0 183 | 184 | 185 | class WareReturnAdmin(generic.BOAdmin): 186 | """ 187 | 188 | """ 189 | CODE_PREFIX = 'FK' 190 | CODE_NUMBER_WIDTH = 4 191 | list_display = ['code','title','out'] 192 | fields = ( 193 | ('code',),('title',),('out',),('amount',),('status',) 194 | ) 195 | readonly_fields = ['status'] 196 | raw_id_fields = ['out'] 197 | inlines = [ReturnItemInline] 198 | extra_buttons = [{'href':'cin','title':_('Action Ware Return')}] 199 | 200 | def changeform_view(self, request, object_id=None, form_url='', extra_context=None): 201 | if object_id : 202 | obj = WareReturn.objects.get(id=object_id) 203 | if obj.status == '9': 204 | extra_context = extra_context or {} 205 | extra_context.update(dict(readonly=True)) 206 | return super(WareReturnAdmin,self).changeform_view(request,object_id,form_url,extra_context) 207 | 208 | 209 | class AdjustItemInline(admin.TabularInline): 210 | model = AdjustItem 211 | fields = ['inventory','measure','warehouse','prop','cnt'] 212 | readonly_fields = ['measure','warehouse'] 213 | raw_id_fields = ['inventory'] 214 | 215 | def get_extra(self, request, obj=None, **kwargs): 216 | if obj: 217 | return 0 218 | else: 219 | return 1 220 | 221 | 222 | class WareAdjustAdmin(generic.BOAdmin): 223 | CODE_PREFIX = 'TZ' 224 | CODE_NUMBER_WIDTH = 3 225 | list_display = ['code','title','status'] 226 | fields = ( 227 | ('code',),('title',),('description',),('status',) 228 | ) 229 | readonly_fields = ['status'] 230 | inlines = [AdjustItemInline] 231 | extra_buttons = [{'href':'adjust','title':_('Action Ware Adjust')}] 232 | date_hierarchy = 'begin' 233 | 234 | def changeform_view(self, request, object_id=None, form_url='', extra_context=None): 235 | if object_id: 236 | obj = WareAdjust.objects.get(id=object_id) 237 | if obj and obj.status == '9': 238 | extra_context = extra_context or {} 239 | extra_context.update(dict(readonly=True)) 240 | return super(WareAdjustAdmin,self).changeform_view(request,object_id,form_url,extra_context) 241 | 242 | 243 | admin.site.register(StockIn,StockInAdmin) 244 | admin.site.register(StockOut,StockOutAdmin) 245 | admin.site.register(InitialInventory,InitialInventoryAdmin) 246 | admin.site.register(Inventory,InventoryAdmin) 247 | admin.site.register(WareReturn,WareReturnAdmin) 248 | admin.site.register(WareAdjust,WareAdjustAdmin) 249 | -------------------------------------------------------------------------------- /invent/apps.py: -------------------------------------------------------------------------------- 1 | # created at 15-5-23 2 | # coding=utf-8 3 | __author__ = 'zhugl' 4 | 5 | from django.apps import AppConfig 6 | from django.utils.translation import ugettext_lazy as _ 7 | 8 | 9 | class MyAppConfig(AppConfig): 10 | name = 'invent' 11 | verbose_name = _("inventory manage") 12 | -------------------------------------------------------------------------------- /invent/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /invent/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import include, url,static 2 | import invent.views 3 | 4 | urlpatterns = [ 5 | url(r"stockin/(?P\d+)/cin", 'invent.views.action_in'), 6 | url(r"initialinventory/(?P\d+)/cin", 'invent.views.action_init'), 7 | url(r"stockout/(?P\d+)/out", 'invent.views.action_out'), 8 | url(r"warereturn/(?P\d+)/cin", 'invent.views.action_return'), 9 | url(r"wareadjust/(?P\d+)/adjust", 'invent.views.action_adjust'), 10 | ] 11 | -------------------------------------------------------------------------------- /invent/views.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from django.contrib.admin import site 3 | from django.contrib.contenttypes.models import ContentType 4 | from django.db import connection 5 | from django.http.response import HttpResponseRedirect 6 | from django.utils.encoding import force_text 7 | from django.template.response import TemplateResponse 8 | from django.contrib import messages 9 | from django.contrib.auth.models import User 10 | from invent.models import StockIn,StockOut,InitialInventory,WareReturn,WareAdjust 11 | from django.utils.translation import ugettext_lazy as _ 12 | 13 | 14 | def action_in(request,object_id): 15 | """ 16 | 入库操作 17 | :param request: 18 | :param object_id: 19 | :return: 20 | """ 21 | title = _("Are you sure?") 22 | obj = StockIn.objects.get(id=int(object_id)) 23 | opts = obj._meta 24 | objects_name = force_text(opts.verbose_name) 25 | 26 | if request.POST.get("post"): 27 | try: 28 | obj.action_entry(request) 29 | messages.success(request,_('check in successfully')) 30 | except Exception,e: 31 | messages.error(request,e) 32 | 33 | return HttpResponseRedirect("/admin/invent/stockin/%s"%(object_id)) 34 | 35 | context = dict( 36 | site.each_context(request), 37 | title=title, 38 | opts=opts, 39 | objects_name=objects_name, 40 | object=obj, 41 | ) 42 | request.current_app = site.name 43 | 44 | return TemplateResponse(request,'admin/invent/stockin/in_confirmation.html', context) 45 | 46 | 47 | def action_out(request,object_id): 48 | """ 49 | 出库操作 50 | :param request: 51 | :param object_id: 52 | :return: 53 | """ 54 | title = _("Are you sure?") 55 | obj = StockOut.objects.get(id=int(object_id)) 56 | opts = obj._meta 57 | objects_name = force_text(opts.verbose_name) 58 | 59 | if request.POST.get("post"): 60 | try: 61 | obj.action_out(request) 62 | messages.success(request,_('check out successfully')) 63 | except Exception,e: 64 | messages.error(request,e) 65 | return HttpResponseRedirect("/admin/invent/stockout/%s"%(object_id)) 66 | 67 | context = dict( 68 | site.each_context(request), 69 | title=title, 70 | opts=opts, 71 | objects_name=objects_name, 72 | object=obj, 73 | ) 74 | request.current_app = site.name 75 | 76 | return TemplateResponse(request,'admin/invent/stockout/out_confirmation.html', context) 77 | 78 | 79 | def action_init(request,object_id): 80 | """ 81 | 期初入库操作 82 | :param request: 83 | :param object_id: 84 | :return: 85 | """ 86 | title = _("Are you sure?") 87 | obj = InitialInventory.objects.get(id=int(object_id)) 88 | opts = obj._meta 89 | objects_name = force_text(opts.verbose_name) 90 | 91 | if request.POST.get("post"): 92 | try: 93 | obj.init_entry(request) 94 | messages.success(request,_('check in successfully')) 95 | except Exception,e: 96 | messages.error(request,e) 97 | 98 | return HttpResponseRedirect("/admin/invent/initialinventory/%s"%(object_id)) 99 | 100 | context = dict( 101 | site.each_context(request), 102 | title=title, 103 | opts=opts, 104 | objects_name=objects_name, 105 | object=obj, 106 | ) 107 | request.current_app = site.name 108 | 109 | return TemplateResponse(request,'admin/invent/stockin/in_confirmation.html', context) 110 | 111 | 112 | def action_return(request,object_id): 113 | """ 114 | 返库操作 115 | :param request: 116 | :param object_id: 117 | :return: 118 | """ 119 | title = _("Are you sure?") 120 | obj = WareReturn.objects.get(id=int(object_id)) 121 | opts = obj._meta 122 | objects_name = force_text(opts.verbose_name) 123 | 124 | if request.POST.get("post"): 125 | try: 126 | obj.action_return(request) 127 | messages.success(request,_('check in successfully')) 128 | except Exception,e: 129 | messages.error(request,e) 130 | 131 | return HttpResponseRedirect("/admin/invent/warereturn/%s"%(object_id)) 132 | 133 | context = dict( 134 | site.each_context(request), 135 | title=title, 136 | opts=opts, 137 | objects_name=objects_name, 138 | object=obj, 139 | ) 140 | request.current_app = site.name 141 | 142 | return TemplateResponse(request,'admin/invent/stockin/in_confirmation.html', context) 143 | 144 | 145 | def action_adjust(request,object_id): 146 | """ 147 | 调整操作 148 | :param request: 149 | :param object_id: 150 | :return: 151 | """ 152 | title = _("Are you sure?") 153 | obj = WareAdjust.objects.get(id=int(object_id)) 154 | opts = obj._meta 155 | objects_name = force_text(opts.verbose_name) 156 | 157 | if request.POST.get("post"): 158 | try: 159 | obj.action_adjust(request) 160 | messages.success(request,_('check in successfully')) 161 | except Exception,e: 162 | messages.error(request,e) 163 | 164 | return HttpResponseRedirect("/admin/invent/wareadjust/%s"%(object_id)) 165 | 166 | context = dict( 167 | site.each_context(request), 168 | title=title, 169 | opts=opts, 170 | objects_name=objects_name, 171 | object=obj, 172 | ) 173 | request.current_app = site.name 174 | 175 | return TemplateResponse(request,'admin/invent/stockin/in_confirmation.html', context) -------------------------------------------------------------------------------- /locale/zh_CN/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/locale/zh_CN/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /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", "mis.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /midware/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'Administrator' 2 | -------------------------------------------------------------------------------- /midware/cuser.py: -------------------------------------------------------------------------------- 1 | __author__ = 'zhugl' 2 | # created at 15-4-21 3 | #python import 4 | from threading import local 5 | from django.contrib import admin 6 | from django.apps import apps 7 | from django.conf import settings 8 | from django.contrib.admin import ModelAdmin, actions 9 | from django.contrib.auth import REDIRECT_FIELD_NAME 10 | from django.core.exceptions import ImproperlyConfigured, PermissionDenied 11 | from django.core.urlresolvers import NoReverseMatch, reverse 12 | from django.db.models.base import ModelBase 13 | from django.http import Http404, HttpResponseRedirect 14 | from django.template.engine import Engine 15 | from django.template.response import TemplateResponse 16 | from django.utils import six 17 | from django.utils.text import capfirst 18 | from django.utils.translation import ugettext as _, ugettext_lazy 19 | from django.views.decorators.cache import never_cache 20 | from django.views.decorators.csrf import csrf_protect 21 | 22 | _thread_local = local() 23 | 24 | 25 | def getuser(): 26 | return getattr(_thread_local,'user',None) 27 | 28 | 29 | class RequestUser(object): 30 | 31 | def process_request(self,request): 32 | django_user = getattr(request,'user',None) 33 | 34 | if django_user is not None: 35 | _thread_local.user = django_user 36 | 37 | def process_view(self, request, view_func, view_args, view_kwargs): 38 | app_weight = {'selfhelp':1,'purchase':3,'sale':2,'invent':4,'organ':5,'basedata':6,'syscfg':7,'workflow':8} 39 | if view_func.__name__ == 'index': 40 | app_dict = {} 41 | for model, model_admin in admin.site._registry.items(): 42 | app_label = model._meta.app_label 43 | has_module_perms = model_admin.has_module_permission(request) 44 | 45 | if has_module_perms: 46 | perms = model_admin.get_model_perms(request) 47 | if True in perms.values(): 48 | info = (app_label, model._meta.model_name) 49 | model_dict = { 50 | 'name': capfirst(model._meta.verbose_name_plural), 51 | 'object_name': model._meta.object_name, 52 | 'perms': perms, 53 | 'weight':getattr(model,'index_weight',99) 54 | } 55 | if perms.get('change', False): 56 | try: 57 | model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=admin.site.name) 58 | except NoReverseMatch: 59 | pass 60 | if perms.get('add', False): 61 | try: 62 | model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=admin.site.name) 63 | except NoReverseMatch: 64 | pass 65 | if app_label in app_dict: 66 | app_dict[app_label]['models'].append(model_dict) 67 | else: 68 | app_dict[app_label] = { 69 | 'name': apps.get_app_config(app_label).verbose_name, 70 | 'app_label': app_label, 71 | 'app_url': reverse( 72 | 'admin:app_list', 73 | kwargs={'app_label': app_label}, 74 | current_app=admin.site.name, 75 | ), 76 | 'has_module_perms': has_module_perms, 77 | 'models': [model_dict], 78 | 'weight':app_weight.get(app_label,99) 79 | } 80 | 81 | app_list = list(six.itervalues(app_dict)) 82 | app_list.sort(key=lambda x: x['weight']) 83 | 84 | for app in app_list: 85 | app['models'].sort(key=lambda x: x['weight']) 86 | 87 | context = dict( 88 | maxi_app_list=app_list, 89 | ) 90 | try: 91 | todolist = self.get_my_task(request) 92 | context.update(dict(todolist = todolist)) 93 | except Exception,e: 94 | pass 95 | # print context 96 | view_kwargs['extra_context'] = context 97 | 98 | if view_func.__name__ == 'app_index': 99 | app_label = view_kwargs['app_label'] 100 | app_name = apps.get_app_config(app_label).verbose_name 101 | app_dict = {} 102 | lib_dict = {} 103 | for model, model_admin in admin.site._registry.items(): 104 | if model_admin.has_module_permission(request): 105 | label = model._meta.app_label 106 | is_current = False 107 | if label == app_label: 108 | is_current = True 109 | lib_dict[label] = { 110 | 'name': apps.get_app_config(label).verbose_name, 111 | 'app_label': label, 112 | 'app_url': reverse( 113 | 'admin:app_list', 114 | kwargs={'app_label': label}, 115 | current_app=admin.site.name, 116 | ), 117 | 'weight':app_weight.get(label,99), 118 | 'is_current':is_current, 119 | } 120 | if app_label == model._meta.app_label: 121 | has_module_perms = model_admin.has_module_permission(request) 122 | if not has_module_perms: 123 | raise PermissionDenied 124 | 125 | perms = model_admin.get_model_perms(request) 126 | 127 | if True in perms.values(): 128 | info = (app_label, model._meta.model_name) 129 | model_dict = { 130 | 'name': capfirst(model._meta.verbose_name_plural), 131 | 'object_name': model._meta.object_name, 132 | 'perms': perms, 133 | 'weight':getattr(model,'index_weight',99) 134 | } 135 | if perms.get('change'): 136 | try: 137 | model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=admin.site.name) 138 | except NoReverseMatch: 139 | pass 140 | if perms.get('add'): 141 | try: 142 | model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=admin.site.name) 143 | except NoReverseMatch: 144 | pass 145 | if app_dict: 146 | app_dict['models'].append(model_dict), 147 | else: 148 | app_dict = { 149 | 'name': app_name, 150 | 'app_label': app_label, 151 | 'app_url': '', 152 | 'has_module_perms': has_module_perms, 153 | 'models': [model_dict], 154 | } 155 | if not app_dict: 156 | raise Http404('The requested admin page does not exist.') 157 | # Sort the models alphabetically within each app. 158 | app_dict['models'].sort(key=lambda x: x['weight']) 159 | 160 | app_lib = list(six.itervalues(lib_dict)) 161 | app_lib.sort(key=lambda x: x['weight']) 162 | 163 | context = dict( 164 | maxi_app_list=[app_dict], 165 | app_lib=app_lib, 166 | ) 167 | view_kwargs['extra_context'] = context 168 | 169 | def get_my_task(self,request): 170 | from workflow.models import TodoList 171 | if request and request.user: 172 | query = TodoList.objects.filter(user=request.user,status=0) 173 | if query.count() == 0: 174 | return None 175 | else: 176 | return query.all()[:10] 177 | -------------------------------------------------------------------------------- /mis/__init__.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | admin.site.site_header = 'Django-ERP' 4 | admin.site.site_title = 'ERP' -------------------------------------------------------------------------------- /mis/production.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for mis project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.8. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.8/ref/settings/ 11 | """ 12 | 13 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 14 | import os 15 | 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | # Quick-start development settings - unsuitable for production 19 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ 20 | 21 | # SECURITY WARNING: keep the secret key used in production secret! 22 | SECRET_KEY = '_5%1a5zxdjsb-je@85!l34g--ve7!skhc%^c2n)3vqyhq)yq@c' 23 | 24 | # SECURITY WARNING: don't run with debug turned on in production! 25 | DEBUG = False 26 | 27 | ALLOWED_HOSTS = ['127.0.0.1', 'localhost'] 28 | 29 | 30 | # Application definition 31 | 32 | INSTALLED_APPS = ( 33 | 'django.contrib.admin', 34 | 'django.contrib.auth', 35 | 'django.contrib.contenttypes', 36 | 'django.contrib.sessions', 37 | 'django.contrib.messages', 38 | 'django.contrib.staticfiles', 39 | 'syscfg', 40 | 'basedata', 41 | 'organ', 42 | 'workflow', 43 | 'selfhelp', 44 | 'hr', 45 | 'invent', 46 | 'purchase', 47 | 'sale', 48 | ) 49 | 50 | MIDDLEWARE_CLASSES = ( 51 | 'django.contrib.sessions.middleware.SessionMiddleware', 52 | 'django.middleware.common.CommonMiddleware', 53 | 'django.middleware.csrf.CsrfViewMiddleware', 54 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 55 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 56 | 'django.contrib.messages.middleware.MessageMiddleware', 57 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 58 | 'django.middleware.security.SecurityMiddleware', 59 | 'midware.cuser.RequestUser', 60 | ) 61 | 62 | ROOT_URLCONF = 'mis.urls' 63 | 64 | TEMPLATES = [ 65 | { 66 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 67 | 'DIRS': [os.path.join(BASE_DIR, 'templates')], 68 | 'APP_DIRS': True, 69 | 'OPTIONS': { 70 | 'context_processors': [ 71 | 'django.template.context_processors.debug', 72 | 'django.template.context_processors.request', 73 | 'django.contrib.auth.context_processors.auth', 74 | 'django.contrib.messages.context_processors.messages', 75 | ], 76 | }, 77 | }, 78 | ] 79 | 80 | TEMPLATE_THEME = 'default' 81 | 82 | WSGI_APPLICATION = 'mis.wsgi.application' 83 | 84 | 85 | # Database 86 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases 87 | 88 | DATABASES = { 89 | 'default': { 90 | 'ENGINE': 'django.db.backends.mysql', 91 | 'HOST': '172.31.16.165', 92 | 'NAME': 'erp', 93 | 'USER': 'maximus', 94 | 'PASSWORD': 'maximus1234', 95 | } 96 | } 97 | 98 | 99 | # Internationalization 100 | # https://docs.djangoproject.com/en/1.8/topics/i18n/ 101 | 102 | LANGUAGE_CODE = 'zh-CN' 103 | 104 | LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale')] 105 | 106 | TIME_ZONE = 'UTC' 107 | 108 | USE_I18N = True 109 | 110 | USE_L10N = True 111 | 112 | USE_TZ = False 113 | 114 | 115 | # Static files (CSS, JavaScript, Images) 116 | # https://docs.djangoproject.com/en/1.8/howto/static-files/ 117 | 118 | STATIC_URL = '/static/' 119 | STATIC_ROOT = os.path.join(BASE_DIR,'static') 120 | STATICFILES_DIRS = ( 121 | ('css',os.path.join(STATIC_ROOT,'css')), 122 | ('js',os.path.join(STATIC_ROOT,'js')), 123 | ('img',os.path.join(STATIC_ROOT,'img')), 124 | ) 125 | 126 | MEDIA_ROOT = os.path.join(BASE_DIR,'upload') 127 | MEDIA_URL = '/upload/' -------------------------------------------------------------------------------- /mis/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for mis project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.8. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.8/ref/settings/ 11 | """ 12 | 13 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 14 | import os 15 | 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | # Quick-start development settings - unsuitable for production 19 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ 20 | 21 | # SECURITY WARNING: keep the secret key used in production secret! 22 | SECRET_KEY = '_5%1a5zxdjsb-je@85!l34g--ve7!skhc%^c2n)3vqyhq)yq@c' 23 | 24 | # SECURITY WARNING: don't run with debug turned on in production! 25 | DEBUG = True 26 | 27 | ALLOWED_HOSTS = ['127.0.0.1', 'localhost'] 28 | 29 | 30 | # Application definition 31 | 32 | INSTALLED_APPS = ( 33 | 'django.contrib.admin', 34 | 'django.contrib.auth', 35 | 'django.contrib.contenttypes', 36 | 'django.contrib.sessions', 37 | 'django.contrib.messages', 38 | 'django.contrib.staticfiles', 39 | 'syscfg', 40 | 'basedata', 41 | 'organ', 42 | 'workflow', 43 | 'selfhelp', 44 | 'hr', 45 | 'invent', 46 | 'purchase', 47 | 'sale', 48 | ) 49 | 50 | MIDDLEWARE_CLASSES = ( 51 | 'django.contrib.sessions.middleware.SessionMiddleware', 52 | 'django.middleware.common.CommonMiddleware', 53 | 'django.middleware.csrf.CsrfViewMiddleware', 54 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 55 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 56 | 'django.contrib.messages.middleware.MessageMiddleware', 57 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 58 | 'django.middleware.security.SecurityMiddleware', 59 | 'midware.cuser.RequestUser', 60 | ) 61 | 62 | ROOT_URLCONF = 'mis.urls' 63 | 64 | TEMPLATES = [ 65 | { 66 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 67 | 'DIRS': [os.path.join(BASE_DIR, 'templates')], 68 | 'APP_DIRS': True, 69 | 'OPTIONS': { 70 | 'context_processors': [ 71 | 'django.template.context_processors.debug', 72 | 'django.template.context_processors.request', 73 | 'django.contrib.auth.context_processors.auth', 74 | 'django.contrib.messages.context_processors.messages', 75 | ], 76 | }, 77 | }, 78 | ] 79 | 80 | TEMPLATE_THEME = 'default' 81 | 82 | WSGI_APPLICATION = 'mis.wsgi.application' 83 | 84 | 85 | # Database 86 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases 87 | 88 | DATABASES = { 89 | 'default': { 90 | 'ENGINE': 'django.db.backends.mysql', 91 | 'HOST': 'localhost', 92 | 'NAME': 'mis', 93 | 'USER': 'root', 94 | 'PASSWORD': 'root', 95 | } 96 | } 97 | 98 | 99 | # Internationalization 100 | # https://docs.djangoproject.com/en/1.8/topics/i18n/ 101 | 102 | LANGUAGE_CODE = 'zh-CN' 103 | 104 | LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale')] 105 | 106 | TIME_ZONE = 'Asia/Shanghai' 107 | 108 | USE_I18N = True 109 | 110 | USE_L10N = True 111 | 112 | USE_TZ = False 113 | 114 | 115 | # Static files (CSS, JavaScript, Images) 116 | # https://docs.djangoproject.com/en/1.8/howto/static-files/ 117 | 118 | STATIC_URL = '/static/' 119 | STATIC_ROOT = os.path.join(BASE_DIR,'static') 120 | STATICFILES_DIRS = ( 121 | ('css',os.path.join(STATIC_ROOT,'css')), 122 | ('js',os.path.join(STATIC_ROOT,'js')), 123 | ('img',os.path.join(STATIC_ROOT,'img')), 124 | ) 125 | 126 | MEDIA_ROOT = os.path.join(BASE_DIR,'upload') 127 | MEDIA_URL = '/upload/' -------------------------------------------------------------------------------- /mis/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import include, url,static 2 | from django.contrib import admin 3 | from mis import settings 4 | import workflow 5 | import invent.urls 6 | import basedata.urls 7 | import selfhelp.urls 8 | import mis 9 | 10 | urlpatterns = [ 11 | # Examples: 12 | url(r'^$', 'mis.views.home'), 13 | # url(r'^blog/', include('blog.urls')), 14 | url(r"^admin/(?P\w+)/(?P\w+)/(?P\d+)/start",'workflow.views.start'), 15 | url(r"^admin/(?P\w+)/(?P\w+)/(?P\d+)/approve/(?P\d+)",'workflow.views.approve'), 16 | url(r"^admin/(?P\w+)/(?P\w+)/(?P\d+)/restart/(?P\d+)",'workflow.views.restart'), 17 | url(r'^admin/', include(admin.site.urls)), 18 | url(r'^admin/invent/', include(invent.urls)), 19 | url(r'^admin/basedata/', include(basedata.urls)), 20 | url(r'^admin/selfhelp/', include(selfhelp.urls)), 21 | ] 22 | urlpatterns += static.static(settings.STATIC_URL,document_root=settings.STATIC_ROOT) 23 | urlpatterns += static.static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT) 24 | -------------------------------------------------------------------------------- /mis/views.py: -------------------------------------------------------------------------------- 1 | from django.http.response import HttpResponseRedirect 2 | 3 | 4 | def home(request): 5 | return HttpResponseRedirect("/admin") -------------------------------------------------------------------------------- /mis/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for mis project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mis.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /organ/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'organ.apps.OrganConfig' -------------------------------------------------------------------------------- /organ/admin.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from django.contrib import admin 3 | from common import generic 4 | from organ.models import Organization,OrgUnit,Position 5 | from basedata.models import BankAccount 6 | from basedata.admin import BankAccountInline 7 | import datetime 8 | 9 | 10 | class OrgAdmin(generic.BOAdmin): 11 | CODE_PREFIX = 'O' 12 | CODE_NUMBER_WIDTH = 2 13 | list_display = ['code','name','represent','lic_code','cer_code'] 14 | 15 | fields = ( 16 | ('name','code',),('short','pinyin',), 17 | ('tax_num','tax_account',),('tax_address',),('represent','email',), 18 | ('address','zipcode',),('contacts','phone',),('fax','website',), 19 | ('lic_code','cer_code',),('license','certificate',),('status','weight',), 20 | ) 21 | 22 | inlines = [BankAccountInline] 23 | 24 | 25 | class OrgUnitAdmin(generic.BOAdmin): 26 | CODE_PREFIX = 'S' 27 | CODE_NUMBER_WIDTH = 3 28 | list_display = ['code','name','unit_type','parent'] 29 | list_display_links = ['code','name'] 30 | fields = ( 31 | ('organization',),('parent',),('name','code',),('short','pinyin',), 32 | ('unit_type',),('status','virtual',),('phone','fax',), 33 | ('contacts','email',),('weight',), 34 | ) 35 | 36 | def get_queryset(self, request): 37 | return super(OrgUnitAdmin,self).get_queryset(request).filter(end__gt=datetime.date.today()) 38 | 39 | def save_model(self, request, obj, form, change): 40 | if obj.parent and obj.parent.organization: 41 | obj.organization = obj.parent.organization 42 | obj.save() 43 | super(OrgUnitAdmin,self).save_model(request,obj,form,change) 44 | 45 | 46 | class PositionAdmin(generic.BOAdmin): 47 | CODE_PREFIX = 'P' 48 | CODE_NUMBER_WIDTH = 4 49 | list_display = ['code','name','unit','series','grade','parent'] 50 | list_display_links = ['code','name'] 51 | fields = ( 52 | ('unit',),('organization',),('parent',),('name','code',),('short','pinyin',),('series','grade',), 53 | ('virtual','status'),('description',),('qualification',),('document',),('weight',), 54 | ) 55 | readonly_fields = ['organization'] 56 | 57 | def get_queryset(self, request): 58 | return super(PositionAdmin,self).get_queryset(request).filter(end__gt=datetime.date.today()) 59 | 60 | def save_model(self, request, obj, form, change): 61 | if obj.unit: 62 | obj.organization = obj.unit.organization 63 | obj.save() 64 | super(PositionAdmin,self).save_model(request,obj,form,change) 65 | 66 | admin.site.register(Position,PositionAdmin) 67 | admin.site.register(OrgUnit,OrgUnitAdmin) 68 | admin.site.register(Organization,OrgAdmin) -------------------------------------------------------------------------------- /organ/apps.py: -------------------------------------------------------------------------------- 1 | __author__ = 'zhugl' 2 | # created at 15-4-22 3 | from django.apps.config import AppConfig 4 | from django.utils.translation import ugettext_lazy as _ 5 | 6 | 7 | class OrganConfig(AppConfig): 8 | name = 'organ' 9 | verbose_name = _('organization') -------------------------------------------------------------------------------- /organ/models.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from django.db import models 3 | from django.db import connection 4 | from django.utils.translation import ugettext_lazy as _ 5 | from common import const 6 | from common import generic 7 | 8 | 9 | class Organization(generic.BO): 10 | """ 11 | 组织单位 12 | """ 13 | index_weight = 1 14 | code = models.CharField(_("organ code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True) 15 | name = models.CharField(_("organ name"),max_length=const.DB_CHAR_NAME_120) 16 | short = models.CharField(_("short name"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True) 17 | pinyin = models.CharField(_("pinyin"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True) 18 | status = models.BooleanField(_("in use"),default=True) 19 | 20 | tax_num = models.CharField(_("tax num"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True) 21 | tax_address = models.CharField(_("tax address"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True) 22 | tax_account = models.CharField(_("tax account"),max_length=const.DB_CHAR_NAME_80,blank=True,null=True) 23 | 24 | represent = models.CharField(_("representative "),max_length=const.DB_CHAR_NAME_40,blank=True,null=True) 25 | address = models.CharField(_("address"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True) 26 | zipcode = models.CharField(_("zipcode"),max_length=const.DB_CHAR_CODE_8,blank=True,null=True) 27 | fax = models.CharField(_("fax"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True) 28 | contacts = models.CharField(_("contacts"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True) 29 | phone = models.CharField(_("phone"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True) 30 | website = models.CharField(_("website"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True) 31 | email = models.CharField(_("email"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True) 32 | lic_code = models.CharField(_("license code"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True) 33 | cer_code = models.CharField(_("certificate code"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True) 34 | license = models.FileField(_("business license"),blank=True,null=True,upload_to='organ') 35 | certificate = models.FileField(_("organization code certificate"),blank=True,null=True,upload_to='organ') 36 | weight = models.IntegerField(_("weight"),default=9) 37 | 38 | class Meta: 39 | verbose_name = _('organization') 40 | verbose_name_plural = _('organization') 41 | 42 | 43 | class OrgUnit(generic.BO): 44 | """ 45 | 组织单元 46 | """ 47 | UNIT_LEVEL = ( 48 | (1,_('BRANCH')), 49 | (2,_('DEPARTMENT')), 50 | (3,_('OFFICE')), 51 | (4,_('TEAM')), 52 | (5,_('COMMITTEE')) 53 | ) 54 | index_weight = 2 55 | parent = models.ForeignKey('self',verbose_name=_("parent"),null=True,blank=True) 56 | organization = models.ForeignKey(Organization,verbose_name = _('organization'),null=True,blank=True) 57 | code = models.CharField(_("code"),max_length=const.DB_CHAR_CODE_8,blank=True,null=True) 58 | name = models.CharField(_("name"),max_length=const.DB_CHAR_NAME_120) 59 | short = models.CharField(_("short name"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True) 60 | pinyin = models.CharField(_("pinyin"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True) 61 | unit_type = models.IntegerField(_("type"),choices=UNIT_LEVEL,default=2) 62 | status = models.BooleanField(_("in use"),default=True) 63 | virtual = models.BooleanField(_("is virtual"),default=False) 64 | fax = models.CharField(_("fax"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True) 65 | phone = models.CharField(_("phone"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True) 66 | contacts = models.CharField(_("contacts"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True) 67 | email = models.CharField(_("email"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True) 68 | weight = models.IntegerField(_("weight"),default=99) 69 | 70 | class Meta: 71 | verbose_name = _('org unit') 72 | verbose_name_plural = _('org unit') 73 | 74 | 75 | class Position(generic.BO): 76 | """ 77 | 岗位 78 | """ 79 | SERIES = ( 80 | ('A',_("Admin Position")), 81 | ('S',_("Sale Position")), 82 | ('T',_("Technology Position")), 83 | ('P',_("Produce Position")), 84 | ) 85 | 86 | GRADE = ( 87 | ('01', _("BASIC")), 88 | ('02', _("MEDIUM")), 89 | ('03', _("SENIOR")), 90 | ('04', _("PROFESSOR")), 91 | ('05', _("EXPERT")), 92 | ) 93 | index_weight = 3 94 | unit = models.ForeignKey(OrgUnit,verbose_name=_('org unit')) 95 | organization = models.ForeignKey(Organization,verbose_name=_('organization'),null=True,blank=True) 96 | parent = models.ForeignKey('self',verbose_name=_("parent"),null=True,blank=True) 97 | code = models.CharField(_("position code"),max_length=const.DB_CHAR_CODE_8,blank=True,null=True) 98 | name = models.CharField(_("position name"),max_length=const.DB_CHAR_NAME_120) 99 | short = models.CharField(_("short name"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True) 100 | pinyin = models.CharField(_("pinyin"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True) 101 | series = models.CharField(_("position series"),max_length=1,default='A',choices=const.get_value_list('S014')) 102 | grade = models.CharField(_("position grade"),max_length=const.DB_CHAR_CODE_2,default='01',choices=const.get_value_list('S015')) 103 | virtual = models.BooleanField(_("is virtual"),default=False) 104 | status = models.BooleanField(_("in use"),default=True) 105 | description = models.TextField(_("position description"),blank=True,null=True) 106 | qualification = models.TextField(_("qualification"),blank=True,null=True) 107 | document = models.FileField(_("reference"),blank=True,null=True) 108 | weight = models.IntegerField(_("weight"),default=99) 109 | 110 | def __unicode__(self): 111 | return u'%s %s' % (self.code,self.name) 112 | 113 | class Meta: 114 | verbose_name = _('position') 115 | verbose_name_plural = _('position') 116 | -------------------------------------------------------------------------------- /organ/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /organ/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /plugin/__init__.py: -------------------------------------------------------------------------------- 1 | # created at 15-6-27 2 | # coding=utf-8 3 | __author__ = 'zhugl' 4 | -------------------------------------------------------------------------------- /plugin/wfactions.py: -------------------------------------------------------------------------------- 1 | # created at 15-6-30 2 | # coding=utf-8 3 | __author__ = 'zhugl' 4 | 5 | 6 | class Operation(object): 7 | APPROVE = 1 8 | DENY = 3 9 | TERMINATE = 4 10 | 11 | 12 | class WorkflowAction(object): 13 | 14 | name = '' 15 | description = '' 16 | 17 | def action(self,request,obj,node_config,operation=Operation.APPROVE): 18 | """ 19 | 20 | :param request: 21 | :param obj: 22 | :param node_config: 23 | :return: 24 | """ 25 | 26 | 27 | class TestAction(WorkflowAction): 28 | name = 'action.test' 29 | 30 | def action(self,request,obj,node_config,operation=Operation.APPROVE): 31 | print 'this is a workflow test action' 32 | print 'request user is %s,current node is %s'%(request.user,node_config) 33 | 34 | 35 | class WorkflowActionManager(object): 36 | """ 37 | 38 | """ 39 | actions = {} 40 | registed = False 41 | 42 | def __init__(self): 43 | if WorkflowActionManager.registed: 44 | pass 45 | else: 46 | WorkflowActionManager.register(TestAction) 47 | WorkflowActionManager.registed = True 48 | 49 | @classmethod 50 | def register(cls,action): 51 | if cls.actions.get(action.name): 52 | raise Exception('%s already exists,register failed'%action.name) 53 | if issubclass(action,WorkflowAction): 54 | WorkflowActionManager.actions[action.name] = action() -------------------------------------------------------------------------------- /plugin/wfnodes.py: -------------------------------------------------------------------------------- 1 | # created at 15-6-30 2 | # coding=utf-8 3 | __author__ = 'zhugl' 4 | from workflow.models import Node 5 | 6 | 7 | class NextNodeHandler(object): 8 | """ 9 | 10 | """ 11 | name = '' 12 | description = '' 13 | 14 | def handle(self,request,obj,node_config): 15 | """ 16 | 17 | :param request: 18 | :param obj: 19 | :param node_config: 20 | :return:workflow.models.Node 21 | """ 22 | return None 23 | 24 | 25 | class TestHandler(NextNodeHandler): 26 | name = 'project.budge.gt.10000' 27 | description = '预算金额大于10000,由总经理审批' 28 | 29 | def handle(self,request,obj,node_config): 30 | budget = getattr(obj,'budget',None) 31 | if budget and budget > 10000: 32 | return Node.objects.filter(id=7).all() 33 | 34 | 35 | class NextNodeManager(object): 36 | """ 37 | 38 | """ 39 | handlers = {} 40 | registed = False 41 | 42 | def __init__(self): 43 | if NextNodeManager.registed: 44 | pass 45 | else: 46 | NextNodeManager.register(TestHandler) 47 | NextNodeManager.registed = True 48 | 49 | @classmethod 50 | def register(cls,handler): 51 | if cls.handlers.get(handler.name): 52 | raise Exception('%s already exists,register failed'%handler.name) 53 | if issubclass(handler,NextNodeHandler): 54 | NextNodeManager.handlers[handler.name] = handler() 55 | -------------------------------------------------------------------------------- /plugin/wfusers.py: -------------------------------------------------------------------------------- 1 | # created at 15-6-30 2 | # coding=utf-8 3 | __author__ = 'zhugl' 4 | 5 | 6 | class NextUserHandler(object): 7 | """ 8 | 9 | """ 10 | name = '' 11 | description = '' 12 | 13 | def handle(self,request,obj,node_config): 14 | """ 15 | 16 | :param request: 17 | :param obj: 18 | :param node_config: 19 | :return:django.contrib.auth.models.User[] 20 | """ 21 | 22 | return None 23 | 24 | 25 | class UpPosition(NextUserHandler): 26 | """ 27 | 28 | """ 29 | name = 'up.position.user' 30 | 31 | def handle(self,request,obj,node_config): 32 | from basedata.models import Employee,Position 33 | emp_query = Employee.objects.filter(user=request.user) 34 | if emp_query.count()>0: 35 | emp = emp_query.all() 36 | parent = [] 37 | for e in emp: 38 | if e.position and e.position.parent: 39 | parent.append(e.position.parent) 40 | # print emp 41 | # print parent 42 | query2 = Employee.objects.filter(position__in=parent).exclude(user=None) 43 | return [x.user for x in query2.all()] 44 | else: 45 | return None 46 | 47 | 48 | class NextUserManager(object): 49 | """ 50 | 51 | """ 52 | handlers = {} 53 | registed = False 54 | 55 | def __init__(self): 56 | if NextUserManager.registed: 57 | pass 58 | else: 59 | NextUserManager.register(UpPosition) 60 | NextUserManager.registed = True 61 | 62 | @classmethod 63 | def register(cls,handler): 64 | if cls.handlers.get(handler.name): 65 | raise Exception('%s already exists,register failed'%handler.name) 66 | if issubclass(handler,NextUserHandler): 67 | NextUserManager.handlers[handler.name] = handler() -------------------------------------------------------------------------------- /plugin/xls.py: -------------------------------------------------------------------------------- 1 | # created at 15-6-27 2 | # coding=utf-8 3 | __author__ = 'zhugl' 4 | import xlrd 5 | import os 6 | import datetime 7 | from mis import settings 8 | 9 | 10 | class Handler(object): 11 | name = '' 12 | 13 | def handle(self,obj,f): 14 | pass 15 | 16 | 17 | class OPSHandler(Handler): 18 | """ 19 | 导入基础信息:部门,岗位,职员 20 | """ 21 | name = 'OPS' 22 | 23 | def handle(self,obj,f): 24 | if f and f.name.endswith('.xls'): 25 | path = os.path.join(settings.MEDIA_ROOT,f.name) 26 | workbook = xlrd.open_workbook(path) 27 | for sheet in workbook.sheets(): 28 | if sheet.name == u'部门' or sheet.name == 'department': 29 | self.department(obj,sheet) 30 | elif sheet.name == u'岗位' or sheet.name == 'position': 31 | self.position(obj,sheet) 32 | elif sheet.name == u'职员' or sheet.name == 'employee': 33 | self.stuff(obj,sheet) 34 | 35 | def department(self,obj,sheet): 36 | from organ.models import OrgUnit 37 | row_count = sheet.nrows 38 | if obj.is_clear: 39 | OrgUnit.objects.update(end=datetime.date.today()) 40 | for row_index in range(1,row_count): 41 | row = sheet.row_values(row_index) 42 | weight = 99 43 | if row[6]: 44 | weight = row[6] 45 | OrgUnit.objects.create(code=row[0],name=row[1],short=row[2],pinyin=row[3],begin=datetime.date.today(), 46 | end=datetime.date(9999,12,31),weight=weight) 47 | for row_index in range(1,row_count): 48 | row = sheet.row_values(row_index) 49 | if len(row[4]) > 0: 50 | try: 51 | parent = OrgUnit.objects.get(code=row[4]) 52 | OrgUnit.objects.filter(code=row[0]).update(parent=parent) 53 | except Exception,e: 54 | pass 55 | 56 | def position(self,obj,sheet): 57 | from organ.models import Position,OrgUnit 58 | row_count = sheet.nrows 59 | if obj.is_clear: 60 | Position.objects.update(end=datetime.date.today()) 61 | for row_index in range(1,row_count): 62 | row = sheet.row_values(row_index) 63 | depart = None 64 | if len(row[4]) > 0: 65 | try: 66 | depart = OrgUnit.objects.filter(code=row[4],end__gt=datetime.date.today()).all()[0] 67 | except Exception,e: 68 | pass 69 | weight = 99 70 | if row[6]: 71 | weight = row[6] 72 | Position.objects.create(code=row[0],name=row[1],unit=depart,begin=datetime.date.today(), 73 | end=datetime.date(9999,12,31),weight=weight) 74 | for row_index in range(1,row_count): 75 | row = sheet.row_values(row_index) 76 | if len(row[2]) > 0: 77 | try: 78 | parent = Position.objects.filter(code=row[2],end__gt=datetime.date.today()).all()[0] 79 | Position.objects.filter(code=row[0]).update(parent=parent) 80 | except Exception,e: 81 | pass 82 | 83 | def stuff(self,obj,sheet): 84 | from organ.models import Position 85 | from basedata.models import Employee 86 | from django.contrib.auth.models import User,Group 87 | row_count = sheet.nrows 88 | try: 89 | group = Group.objects.get_by_natural_key(u'职员') 90 | except Exception,e: 91 | pass 92 | for row_index in range(1,row_count): 93 | row = sheet.row_values(row_index) 94 | position = Position.objects.filter(code=row[8],end__gt=datetime.date.today()).all()[0] 95 | username = row[10] 96 | email = row[11] 97 | password = row[4][-6:] 98 | if position is None: 99 | raise Exception(u'职员%s-%s未分配岗位,或者您选择的岗位已失效,不可被引用'%(row[0],row[1])) 100 | try: 101 | employee = Employee.objects.get(code=row[0]) 102 | if employee.position.code == row[8]: 103 | continue 104 | else: 105 | employee.position = position 106 | employee.save() 107 | except Exception,e: 108 | employee = Employee.objects.create(code=row[0],name=row[1],pinyin=row[2],gender=row[3],idcard=row[4], 109 | birthday=row[5],workday=row[6],startday=row[7],position=position) 110 | if username: 111 | try: 112 | user = User.objects.get_by_natural_key(username) 113 | except Exception,e: 114 | user = User.objects.create_user(username=username,password=password) 115 | user.is_staff = True 116 | user.is_active = True 117 | user.first_name = row[1] 118 | if email: 119 | user.email = email 120 | if group: 121 | user.groups.add(group) 122 | user.save() 123 | employee.user = user 124 | employee.save() 125 | 126 | 127 | class UserHandler(Handler): 128 | """ 129 | 基础数据导入:用户 130 | """ 131 | name = 'admin.user' 132 | 133 | def handle(self,obj,f): 134 | from django.contrib.auth.models import User,Group 135 | if f and f.name.endswith('.xls'): 136 | path = os.path.join(settings.MEDIA_ROOT,f.name) 137 | workbook = xlrd.open_workbook(path) 138 | sheet = workbook.sheet_by_index(0) 139 | row_count = sheet.nrows 140 | try: 141 | group = Group.objects.get_by_natural_key(u'职员') 142 | except Exception,e: 143 | pass 144 | 145 | for row_index in range(2,row_count): 146 | row = sheet.row_values(row_index) 147 | username = row[0] 148 | password = row[1] 149 | last_name = row[2] 150 | first_name = row[3] 151 | email = row[4] 152 | if username == '': 153 | continue 154 | try: 155 | user = User.objects.get_by_natural_key(username) 156 | except Exception,e: 157 | user = User.objects.create_user(username=username,password=password,email=email) 158 | user.is_staff = True 159 | user.is_active = True 160 | user.last_name = last_name 161 | user.first_name = first_name 162 | if group: 163 | user.groups.add(group) 164 | user.save() 165 | 166 | 167 | class ExcelManager(object): 168 | """ 169 | 170 | """ 171 | handlers = {} 172 | 173 | def __init__(self): 174 | ExcelManager.register(OPSHandler) 175 | ExcelManager.register(UserHandler) 176 | 177 | @classmethod 178 | def register(cls,handler): 179 | if cls.handlers.get(handler.name): 180 | raise Exception('%s already exists,register failed'%handler.name) 181 | if issubclass(handler,Handler): 182 | ExcelManager.handlers[handler.name] = handler() 183 | 184 | -------------------------------------------------------------------------------- /purchase/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = "purchase.apps.MyAppConfig" -------------------------------------------------------------------------------- /purchase/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from common import generic 3 | from purchase.models import PurchaseOrder,POItem,Invoice,Payment 4 | from basedata.models import Partner,BankAccount 5 | 6 | 7 | class POItemInline(admin.TabularInline): 8 | model = POItem 9 | fields = ('material', 'measure','price', 'cnt', 'tax', 'discount_price') 10 | raw_id_fields = ['material'] 11 | 12 | def get_extra(self, request, obj=None, **kwargs): 13 | if obj: 14 | return 0 15 | else: 16 | return 3 17 | 18 | 19 | class PurchaseOrderAdmin(generic.BOAdmin): 20 | """ 21 | 22 | """ 23 | CODE_PREFIX = 'CG' 24 | CODE_NUMBER_WIDTH = 5 25 | list_display = ['code','title','order_date','partner','amount','discount_amount','payed_amount','invoice_amount','status'] 26 | list_display_links = ['code','title'] 27 | raw_id_fields = ['partner'] 28 | fields = ( 29 | ('code','partner'),('order_date','arrive_date'), 30 | ('title','status',),('description',),('amount','discount_amount'),('attach',), 31 | ) 32 | readonly_fields = ['status','amount'] 33 | inlines = [POItemInline] 34 | date_hierarchy = 'begin' 35 | 36 | def changeform_view(self, request, object_id=None, form_url='', extra_context=None): 37 | if object_id: 38 | extra_context = extra_context or {} 39 | obj = PurchaseOrder.objects.get(id=object_id) 40 | if obj.status == '99': 41 | extra_context.update(dict(readonly=True)) 42 | return super(PurchaseOrderAdmin,self).changeform_view(request,object_id,form_url,extra_context) 43 | 44 | def get_changeform_initial_data(self, request): 45 | import datetime 46 | begin = datetime.date.today() 47 | end = begin + datetime.timedelta(30) 48 | return {'order_date':begin,'arrive_date':end} 49 | 50 | 51 | class PurchaseItemAdmin(generic.BOAdmin): 52 | list_display = ['po','vender','material','cnt','price','tax'] 53 | readonly_fields = ['po','material','cnt','price','tax'] 54 | 55 | def get_queryset(self, request): 56 | return POItem.objects.filter(left_cnt__gt=0) 57 | 58 | 59 | class InvoiceAdmin(generic.BOAdmin): 60 | list_display = ['code','number','vo_date','po','partner','po_amount','vo_amount'] 61 | readonly_fields = ['partner','po_amount'] 62 | raw_id_fields = ['po'] 63 | search_fields = ['po__code','partner__name'] 64 | date_hierarchy = 'begin' 65 | fields = ( 66 | ('vo_date',),('po',),('po_amount','partner',),('code',),('number',),('vo_amount',),('file',) 67 | ) 68 | 69 | 70 | class PaymentAdmin(generic.BOAdmin): 71 | CODE_PREFIX = 'PY' 72 | CODE_NUMBER_WIDTH = 4 73 | list_display = ['code','py_date','po','partner','po_amount','py_amount'] 74 | readonly_fields = ['partner','po_amount'] 75 | raw_id_fields = ['po'] 76 | search_fields = ['po__code','partner__name'] 77 | fields = ( 78 | ('py_date','code',),('po',),('partner','po_amount',),('py_amount',),('bank',),('response_code'),('memo',) 79 | ) 80 | date_hierarchy = 'begin' 81 | 82 | def formfield_for_foreignkey(self, db_field, request=None, **kwargs): 83 | if db_field.name == 'bank': 84 | kwargs['queryset'] = BankAccount.objects.exclude(org__exact=None) 85 | return super(PaymentAdmin,self).formfield_for_foreignkey(db_field,request,**kwargs) 86 | 87 | admin.site.register(PurchaseOrder,PurchaseOrderAdmin) 88 | admin.site.register(POItem,PurchaseItemAdmin) 89 | admin.site.register(Invoice,InvoiceAdmin) 90 | admin.site.register(Payment,PaymentAdmin) 91 | -------------------------------------------------------------------------------- /purchase/apps.py: -------------------------------------------------------------------------------- 1 | # created at 15-5-23 2 | # coding=utf-8 3 | __author__ = 'zhugl' 4 | 5 | from django.apps import AppConfig 6 | from django.utils.translation import ugettext_lazy as _ 7 | 8 | 9 | class MyAppConfig(AppConfig): 10 | name = 'purchase' 11 | verbose_name = _("purchase manage") 12 | -------------------------------------------------------------------------------- /purchase/models.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import datetime 3 | import os 4 | import xlrd 5 | import decimal 6 | from django.db import transaction 7 | from django.db import models 8 | from django.db.models.aggregates import Sum 9 | from django.contrib.auth.models import User 10 | from django.utils.translation import ugettext_lazy as _ 11 | from django.utils.text import force_text 12 | from mis import settings 13 | from common import generic 14 | from common import const 15 | from selfhelp.models import WOItem 16 | from basedata.models import Material,Organization,Partner,Measure,BankAccount 17 | 18 | 19 | class PurchaseOrder(generic.BO): 20 | """ 21 | 22 | """ 23 | STATUS = ( 24 | ('0', _("NEW")), 25 | ('1', _("IN PROGRESS")), 26 | ('4', _("DROP")), 27 | ('9', _("APPROVED")), 28 | ('99', _("ALREADY STOCK IN")), 29 | ) 30 | index_weight = 1 31 | code = models.CharField(_("code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True) 32 | partner = models.ForeignKey(Partner,verbose_name=_("partner"),limit_choices_to={"partner_type":"S"}) 33 | order_date = models.DateField(_("order date")) 34 | arrive_date = models.DateField(_("arrive date")) 35 | org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True) 36 | title = models.CharField(_("title"),max_length=const.DB_CHAR_NAME_40) 37 | description = models.TextField(_("description"),blank=True,null=True) 38 | user = models.ForeignKey(User,verbose_name=_("user"),blank=True,null=True) 39 | status = models.CharField(_("status"),max_length=const.DB_CHAR_CODE_2,default='0',choices=STATUS) 40 | amount = models.DecimalField(_("money amount"),max_digits=12,decimal_places=2,blank=True,null=True,default=0.00) 41 | discount_amount = models.DecimalField(_("discount amount"),max_digits=12,decimal_places=2,blank=True,null=True,default=0.00) 42 | entry_status = models.BooleanField(_("entry status"),default=0) 43 | entry_time = models.DateTimeField(_("entry time"),blank=True,null=True) 44 | attach = models.FileField(_('attach'),blank=True,null=True,help_text=u'您可导入采购明细,模板请参考文档FD0008') 45 | 46 | def __unicode__(self): 47 | return u'%s %s' % (self.code,self.title) 48 | 49 | def save(self, force_insert=False, force_update=False, using=None, 50 | update_fields=None): 51 | super(PurchaseOrder,self).save(force_insert,force_update,using,update_fields) 52 | if self.discount_amount > 0: 53 | sql = 'UPDATE purchase_poitem a SET a.discount_price = ' \ 54 | 'a.price-((SELECT discount_amount/amount FROM purchase_purchaseorder WHERE id=%s)*a.amount/a.cnt) WHERE a.po_id = %s' 55 | params = [self.id,self.id] 56 | generic.update(sql,params) 57 | 58 | item_count = POItem.objects.filter(po=self).count() 59 | if self.attach and item_count == 0: 60 | path = os.path.join(settings.MEDIA_ROOT,self.attach.name) 61 | workbook = xlrd.open_workbook(path) 62 | sheet = workbook.sheet_by_index(0) 63 | row_count = sheet.nrows 64 | with transaction.atomic(): 65 | total_amount = decimal.Decimal(0) 66 | for row_index in range(row_count): 67 | row = sheet.row_values(row_index) 68 | if row_index == 0: 69 | doc_type = row[1] 70 | if doc_type.startswith('0'): 71 | break 72 | else: 73 | continue 74 | elif row_index < 3: 75 | continue 76 | 77 | material = None 78 | measure = None 79 | 80 | try: 81 | measure = Measure.objects.get(code=row[4]) 82 | except Exception,e: 83 | measure = Measure.objects.create(code=row[4],name=force_text(row[5])) 84 | 85 | try: 86 | material = Material.objects.get(code=row[0]) 87 | except Exception,e: 88 | material = Material(code=row[0],name=force_text(row[1]),spec=force_text(row[2])) 89 | material.purchase_price = row[6] 90 | material.save() 91 | amount = decimal.Decimal(row[6])*decimal.Decimal(row[7]) 92 | POItem.objects.create(po=self,material=material,measure=measure,cnt=row[7],price=row[6],amount=amount) 93 | total_amount += amount 94 | sql = 'update purchase_purchaseorder set amount = %s where id=%s' 95 | params = [total_amount,self.id] 96 | generic.update(sql,params) 97 | 98 | def invoice_amount(self): 99 | total = Invoice.objects.filter(po=self).aggregate(Sum('vo_amount')).get('vo_amount__sum') or 0.00 100 | return total 101 | 102 | def payed_amount(self): 103 | return Payment.objects.filter(po=self).aggregate(Sum('py_amount')).get('py_amount__sum') or 0.00 104 | 105 | invoice_amount.short_description = _('invoice amount') 106 | payed_amount.short_description = _('pay amount') 107 | 108 | class Meta: 109 | verbose_name = _("purchase order") 110 | verbose_name_plural = _("purchase orders") 111 | 112 | 113 | class POItem(models.Model): 114 | """ 115 | 116 | """ 117 | index_weight = 2 118 | po = models.ForeignKey(PurchaseOrder,verbose_name=_("purchase order")) 119 | material = models.ForeignKey(Material,verbose_name=_("material"),limit_choices_to={"is_virtual":"0"}) 120 | measure = models.ForeignKey(Measure,verbose_name=_("measure"),blank=True,null=True) 121 | price = models.DecimalField(_("price"),max_digits=12,decimal_places=4,blank=True,null=True) 122 | cnt = models.DecimalField(_("count"),max_digits=12,decimal_places=4,blank=True,null=True) 123 | discount_price = models.DecimalField(_("discount price"),max_digits=12,decimal_places=4,blank=True,null=True) 124 | amount = models.DecimalField(_("money of amount"),max_digits=12,decimal_places=2,blank=True,null=True) 125 | discount_amount = models.DecimalField(_("discount amount"),max_digits=12,decimal_places=2,blank=True,null=True) 126 | tax = models.CharField(_("tax rate"),max_length=const.DB_CHAR_CODE_6,choices=const.get_value_list('S052'),default='0.00') 127 | woitem = models.ForeignKey(WOItem,verbose_name=_("wo item"),blank=True,null=True) 128 | is_in_stock = models.BooleanField(_("is in stock"),default=0) 129 | in_stock_time = models.DateTimeField(_("execute time"),blank=True,null=True) 130 | entry_cnt = models.DecimalField(_("entry count"),max_digits=12,decimal_places=4,blank=True,null=True) 131 | left_cnt = models.DecimalField(_("left count"),max_digits=12,decimal_places=4,blank=True,null=True) 132 | 133 | def save(self, force_insert=False, force_update=False, using=None, 134 | update_fields=None): 135 | if self.price and self.cnt: 136 | money = self.price * self.cnt 137 | self.amount = money 138 | 139 | if self.measure is None and self.material and self.material.measure.count() > 0: 140 | self.measure = self.material.measure.all()[0] 141 | 142 | if self.is_in_stock: 143 | self.left_cnt -= self.entry_cnt 144 | else: 145 | self.left_cnt = self.cnt 146 | super(POItem,self).save(force_insert,force_update,using,update_fields) 147 | self.material.purchase_price = self.price 148 | self.material.save() 149 | sql = 'UPDATE purchase_purchaseorder SET amount = (SELECT SUM(a.price*a.cnt) AS amount FROM ' \ 150 | 'purchase_poitem a WHERE a.po_id = %s) WHERE id = %s' 151 | params = [self.po.id,self.po.id] 152 | generic.update(sql,params) 153 | 154 | def vender(self): 155 | return u'%s' % (self.po.partner) 156 | 157 | vender.short_description = _("partner") 158 | 159 | class Meta: 160 | verbose_name = _("po item") 161 | verbose_name_plural = _("po item") 162 | 163 | 164 | class Invoice(generic.BO): 165 | """ 166 | 采购发票 167 | """ 168 | index_weight = 4 169 | vo_date = models.DateField(_("invoice date"),blank=True,null=True,default=datetime.date.today) 170 | code = models.CharField(_("invoice code"),max_length=const.DB_CHAR_NAME_20) 171 | number = models.CharField(_("invoice number"),max_length=const.DB_CHAR_NAME_20) 172 | po = models.ForeignKey(PurchaseOrder,verbose_name=_("purchase order")) 173 | partner = models.ForeignKey(Partner,verbose_name=_("partner"),blank=True,null=True) 174 | po_amount = models.DecimalField(_("po amount"),max_digits=14,decimal_places=4,blank=True,null=True) 175 | vo_amount = models.DecimalField(_("invoice amount"),max_digits=14,decimal_places=4) 176 | file = models.FileField(_("invoice file"),upload_to='invoice',blank=True,null=True) 177 | 178 | def __unicode__(self): 179 | return u"%s %s" % (self.code,self.partner) 180 | 181 | def save(self, force_insert=False, force_update=False, using=None, 182 | update_fields=None): 183 | if self.po: 184 | self.partner = self.po.partner 185 | self.po_amount = self.po.amount 186 | super(Invoice,self).save(force_insert,force_update,using,update_fields) 187 | 188 | class Meta: 189 | verbose_name = _("Invoice") 190 | verbose_name_plural = _("Invoice") 191 | 192 | 193 | class Payment(generic.BO): 194 | """ 195 | 采购付款 196 | """ 197 | index_weight = 3 198 | py_date = models.DateField(_("pay date"),blank=True,null=True,default=datetime.date.today) 199 | org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True) 200 | code = models.CharField(_("pay code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True) 201 | po = models.ForeignKey(PurchaseOrder,verbose_name=_("purchase order")) 202 | partner = models.ForeignKey(Partner,verbose_name=_("partner"),blank=True,null=True) 203 | po_amount = models.DecimalField(_("po amount"),max_digits=14,decimal_places=4,blank=True,null=True) 204 | py_amount = models.DecimalField(_("pay amount"),max_digits=14,decimal_places=4) 205 | bank = models.ForeignKey(BankAccount,verbose_name=_("bank account"),blank=True,null=True) 206 | response_code = models.CharField(_("response code"),max_length=const.DB_CHAR_NAME_80,blank=True,null=True) 207 | memo = models.TextField(_("memo"),blank=True,null=True) 208 | 209 | def save(self, force_insert=False, force_update=False, using=None, 210 | update_fields=None): 211 | if self.po: 212 | self.partner = self.po.partner 213 | self.po_amount = self.po.amount 214 | super(Payment,self).save(force_insert,force_update,using,update_fields) 215 | 216 | def __unicode__(self): 217 | return u"%s %s" % (self.code,self.partner) 218 | 219 | class Meta: 220 | verbose_name = _("Payment") 221 | verbose_name_plural = _("Payment") -------------------------------------------------------------------------------- /purchase/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /purchase/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /sale/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = "sale.apps.MyAppConfig" -------------------------------------------------------------------------------- /sale/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from common import generic 3 | from sale.models import SaleOrder,SaleItem,PaymentCollection,OfferSheet,OfferItem 4 | from basedata.models import Measure,BankAccount 5 | from common import generic 6 | import datetime 7 | 8 | 9 | class SaleItemInline(admin.TabularInline): 10 | model = SaleItem 11 | fields = ('material','measure','sale_price','discount_price','cnt','tax') 12 | raw_id_fields = ['material'] 13 | 14 | def get_extra(self, request, obj=None, **kwargs): 15 | if obj: 16 | return 0 17 | else: 18 | return 3 19 | 20 | 21 | class OfferItemInline(admin.TabularInline): 22 | model = OfferItem 23 | fields = ('material','brand','measure','cost_price','sale_price','discount_price','cnt','tax') 24 | raw_id_fields = ['material'] 25 | readonly_fields = ['brand'] 26 | 27 | def get_extra(self, request, obj=None, **kwargs): 28 | if obj: 29 | return 0 30 | else: 31 | return 3 32 | 33 | 34 | class SaleOrderAdmin(generic.BOAdmin): 35 | CODE_NUMBER_WIDTH = 5 36 | CODE_PREFIX = 'SO' 37 | inlines = [SaleItemInline] 38 | list_display = ['code','title','order_date','partner','amount','collection_amount'] 39 | list_display_links = ['code','title'] 40 | raw_id_fields = ['partner','user','org'] 41 | fields = ( 42 | ('code','org',),('title','invoice_type',),('partner','user',),('order_date','deliver_date',), 43 | ('contact','phone',),('deliver_address','fax',),('description',),('amount','discount_amount','status') 44 | ) 45 | readonly_fields = ['amount','status'] 46 | date_hierarchy = 'begin' 47 | 48 | def save_model(self, request, obj, form, change): 49 | if obj: 50 | obj.user = request.user 51 | super(SaleOrderAdmin,self).save_model(request,obj,form,change) 52 | 53 | def get_changeform_initial_data(self, request): 54 | today = datetime.datetime.today() 55 | deadline = today+datetime.timedelta(days=30) 56 | return {'order_date':today,'deliver_date':deadline} 57 | 58 | 59 | class OfferSheetAdmin(generic.BOAdmin): 60 | CODE_NUMBER_WIDTH = 5 61 | CODE_PREFIX = 'BJ' 62 | inlines = [OfferItemInline] 63 | list_display = ['code','title','offer_date','partner','amount'] 64 | list_display_links = ['code','title'] 65 | raw_id_fields = ['partner','user','org'] 66 | fields = ( 67 | ('code','org',),('partner','user',),('offer_date','deliver_date',),('title',), 68 | ('description',),('amount','discount_amount',),('attach'), 69 | ) 70 | readonly_fields = ['amount'] 71 | date_hierarchy = 'begin' 72 | 73 | def save_model(self, request, obj, form, change): 74 | if obj: 75 | obj.user = request.user 76 | super(OfferSheetAdmin,self).save_model(request,obj,form,change) 77 | 78 | def get_changeform_initial_data(self, request): 79 | today = datetime.datetime.today() 80 | deadline = today+datetime.timedelta(days=30) 81 | return {'offer_date':today,'deliver_date':deadline} 82 | 83 | 84 | class PaymentCollectionAdmin(generic.BOAdmin): 85 | CODE_NUMBER_WIDTH = 4 86 | CODE_PREFIX = 'CP' 87 | list_display = ['code','so','partner','order_amount','collection_date','collection_amount'] 88 | fields = ( 89 | ('code',),('collection_date',),('so',),('partner',),('order_amount','collection_amount',),('bank',), 90 | ('memo',) 91 | ) 92 | raw_id_fields = ['so','partner'] 93 | readonly_fields = ['order_amount'] 94 | list_display_links = ['code','so'] 95 | date_hierarchy = 'begin' 96 | 97 | def formfield_for_foreignkey(self, db_field, request=None, **kwargs): 98 | if db_field.name=='bank': 99 | kwargs['queryset'] = BankAccount.objects.exclude(org__exact=None) 100 | return super(PaymentCollectionAdmin,self).formfield_for_foreignkey(db_field,request,**kwargs) 101 | 102 | admin.site.register(SaleOrder,SaleOrderAdmin) 103 | admin.site.register(PaymentCollection,PaymentCollectionAdmin) 104 | admin.site.register(OfferSheet,OfferSheetAdmin) 105 | -------------------------------------------------------------------------------- /sale/apps.py: -------------------------------------------------------------------------------- 1 | # created at 15-5-23 2 | # coding=utf-8 3 | __author__ = 'zhugl' 4 | 5 | from django.apps import AppConfig 6 | from django.utils.translation import ugettext_lazy as _ 7 | 8 | 9 | class MyAppConfig(AppConfig): 10 | name = 'sale' 11 | verbose_name = _("sale management") 12 | -------------------------------------------------------------------------------- /sale/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /sale/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /selfhelp/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = "selfhelp.apps.MyAppConfig" -------------------------------------------------------------------------------- /selfhelp/admin.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | # coding = utf-8 3 | import datetime 4 | from django.contrib import admin 5 | from django.contrib import messages 6 | from django.http import HttpResponseRedirect 7 | from django.db.models.aggregates import Sum 8 | from django.utils.translation import ugettext_lazy as _ 9 | from common import generic 10 | from common import const 11 | from basedata.models import Material,ExtraParam,Employee,Position 12 | from selfhelp.models import WorkOrder,WOExtraValue,WOItem,Reimbursement,ReimbursementItem,Loan,Enroll,Feedback,Activity 13 | 14 | 15 | class ParamValueInline(admin.TabularInline): 16 | model = WOExtraValue 17 | fields = ('param_name','param_value') 18 | readonly_fields = ['param_name'] 19 | 20 | def formfield_for_foreignkey(self, db_field, request=None, **kwargs): 21 | 22 | if db_field.name == 'param_name': 23 | app_info = generic.get_app_model_info_from_request(request) 24 | instance = app_info['obj'] 25 | if instance: 26 | kwargs['queryset'] = ExtraParam.objects.filter(material=instance.service) 27 | 28 | return super(ParamValueInline,self).formfield_for_foreignkey(db_field,request,**kwargs) 29 | 30 | def get_extra(self, request, obj=None, **kwargs): 31 | if obj: 32 | return 0 33 | else: 34 | return 1 35 | 36 | 37 | class ItemInline(admin.TabularInline): 38 | model = WOItem 39 | raw_id_fields = ['material'] 40 | 41 | def get_extra(self, request, obj=None, **kwargs): 42 | if obj: 43 | return 0 44 | else: 45 | return 1 46 | 47 | 48 | class WorkOrderAdmin(generic.BOAdmin): 49 | """ 50 | 51 | """ 52 | CODE_PREFIX = 'WO' 53 | CODE_NUMBER_WIDTH = 5 54 | list_display = ['code','begin','title','classification','business_domain','status'] 55 | list_display_links = ['code','title'] 56 | exclude = ['creator','modifier','creation','modification'] 57 | search_fields = ['code','title'] 58 | list_filter = ['classification','service','status'] 59 | fields = ( 60 | ('begin','end',), 61 | ('code','refer',),('classification','business_domain',), 62 | ('service','project',), 63 | ('title','status',),('description',),('attach',),('detail',) 64 | ) 65 | readonly_fields = ['status'] 66 | raw_id_fields = ['service','project','refer'] 67 | inlines = [ItemInline,ParamValueInline] 68 | date_hierarchy = 'begin' 69 | 70 | def save_model(self, request, obj, form, change): 71 | if obj.user is None: 72 | obj.user = request.user 73 | super(WorkOrderAdmin,self).save_model(request,obj,form,change) 74 | 75 | def get_changeform_initial_data(self, request): 76 | import datetime 77 | td = datetime.date.today() 78 | end = td + datetime.timedelta(30) 79 | return {'begin':datetime.date.today, 'end':end} 80 | 81 | def formfield_for_foreignkey(self, db_field, request=None, **kwargs): 82 | if db_field.name == 'refer': 83 | app_info = generic.get_app_model_info_from_request(request) 84 | if app_info and app_info['obj']: 85 | kwargs['queryset'] = WorkOrder.objects.exclude(id=app_info['id']) 86 | 87 | return super(WorkOrderAdmin,self).formfield_for_foreignkey(db_field,request,**kwargs) 88 | 89 | 90 | class LoanAdmin(generic.BOAdmin): 91 | CODE_PREFIX = 'JK' 92 | CODE_NUMBER_WIDTH = 5 93 | list_display = ['code','title','project','loan_amount','applier','status'] 94 | list_display_links = ['code','title'] 95 | readonly_fields = ['status','logout_time','logout_amount'] 96 | raw_id_fields = ['project','user'] 97 | fields = ( 98 | ('code',),('title','loan_amount',),('description',),('project'),('user','status'),('logout_time','logout_amount',), 99 | ) 100 | extra_buttons = [{'href':'pay','title':_('pay')}] 101 | search_fields = ['code','title','user__username'] 102 | date_hierarchy = 'begin' 103 | 104 | def changeform_view(self, request, object_id=None, form_url='', extra_context=None): 105 | if object_id: 106 | try: 107 | obj = Loan.objects.get(id=object_id) 108 | if obj and obj.status == 'P': 109 | extra_context = extra_context or {} 110 | extra_context.update(dict(readonly=True)) 111 | except Exception,e: 112 | pass 113 | return super(LoanAdmin,self).changeform_view(request,object_id,form_url,extra_context) 114 | 115 | def save_model(self, request, obj, form, change): 116 | if obj and obj.user is None: 117 | obj.user = request.user 118 | super(LoanAdmin,self).save_model(request,obj,form,change) 119 | 120 | 121 | class ReimbursementItemInline(admin.TabularInline): 122 | model = ReimbursementItem 123 | raw_id_fields = ['expense_account'] 124 | 125 | def get_extra(self, request, obj=None, **kwargs): 126 | if obj: 127 | return 0 128 | else: 129 | return 1 130 | 131 | 132 | class ReimbursementAdmin(generic.BOAdmin): 133 | CODE_PREFIX = 'BX' 134 | CODE_NUMBER_WIDTH = 5 135 | list_display = ['code','title','project','amount','applier','status'] 136 | list_display_links = ['code','title'] 137 | inlines = [ReimbursementItemInline] 138 | raw_id_fields = ['project','wo','user','org'] 139 | readonly_fields = ['loan_amount','pay_time','status','amount'] 140 | fieldsets = [ 141 | (None,{'fields':[('code','user'),('title','amount','status',),('description',),('project','wo',)]}), 142 | (_('fico'),{'fields':[('org',),('loan',),('logout_amount','pay_amount',)],'classes': ['collapse']}) 143 | ] 144 | extra_buttons = [{'href':'pay','title':_('pay')}] 145 | search_fields = ['code','title','project__code','project__name','user__username'] 146 | date_hierarchy = 'begin' 147 | 148 | def get_changeform_initial_data(self, request): 149 | apps = generic.get_app_model_info_from_request(request) 150 | obj = getattr(apps,'obj',None) 151 | current = request.user 152 | if obj: 153 | current = obj.user 154 | sm = Loan.objects.filter(user=current).aggregate(Sum('loan_amount')).get('loan_amount__sum') or 0.00 155 | return {'loan_amount':sm} 156 | 157 | def save_model(self, request, obj, form, change): 158 | if obj and obj.user is None: 159 | obj.user = request.user 160 | 161 | super(ReimbursementAdmin,self).save_model(request,obj,form,change) 162 | 163 | def formfield_for_foreignkey(self, db_field, request=None, **kwargs): 164 | if db_field.name == 'loan': 165 | apps = generic.get_app_model_info_from_request(request) 166 | current = request.user 167 | if apps: 168 | obj = apps.get('obj') 169 | current = obj.user 170 | if obj.status == 'P': 171 | kwargs['queryset']=Loan.objects.filter(id=obj.loan.id) 172 | else: 173 | kwargs['queryset']=Loan.objects.filter(user=current,is_clear=0) 174 | else: 175 | kwargs['queryset']=Loan.objects.filter(user=current,is_clear=0) 176 | return super(ReimbursementAdmin,self).formfield_for_foreignkey(db_field,request,**kwargs) 177 | 178 | def changeform_view(self, request, object_id=None, form_url='', extra_context=None): 179 | if object_id: 180 | try: 181 | obj = Reimbursement.objects.get(id=object_id) 182 | if obj and obj.status == 'P': 183 | extra_context = extra_context or {} 184 | extra_context.update(dict(readonly=True)) 185 | except Exception,e: 186 | pass 187 | return super(ReimbursementAdmin,self).changeform_view(request,object_id,form_url,extra_context) 188 | 189 | 190 | class EnrollInline(admin.TabularInline): 191 | model = Enroll 192 | 193 | 194 | class FeedbackInline(admin.TabularInline): 195 | model = Feedback 196 | 197 | 198 | class ActivityAdmin(generic.BOAdmin): 199 | CODE_PREFIX = 'AC' 200 | CODE_NUMBER_WIDTH = 5 201 | list_display = ['code','begin_time','end_time','title','classification','room'] 202 | list_display_links = ['code','title'] 203 | raw_id_fields = ['room','parent'] 204 | fieldsets = [ 205 | (None,{'fields':[('begin_time','end_time',),('title','classification',),('description',), 206 | ('host','speaker',),('room','location',),('attach',)]}), 207 | (_('other info'),{'fields':[('mail_list',),('parent',),('mail_notice','short_message_notice','weixin_notice',)],'classes': ['collapse']}) 208 | ] 209 | 210 | def get_changeform_initial_data(self, request): 211 | now = datetime.datetime.now() 212 | begin = now + datetime.timedelta(hours=12) 213 | end = begin + datetime.timedelta(hours=6) 214 | return {'begin_time':begin,'end_time':end} 215 | 216 | admin.site.register(WorkOrder,WorkOrderAdmin) 217 | admin.site.register(Loan,LoanAdmin) 218 | admin.site.register(Reimbursement,ReimbursementAdmin) 219 | admin.site.register(Activity,ActivityAdmin) 220 | -------------------------------------------------------------------------------- /selfhelp/apps.py: -------------------------------------------------------------------------------- 1 | # created at 15-5-23 2 | # coding=utf-8 3 | __author__ = 'zhugl' 4 | 5 | from django.apps import AppConfig 6 | from django.utils.translation import ugettext_lazy as _ 7 | 8 | 9 | class MyAppConfig(AppConfig): 10 | name = 'selfhelp' 11 | verbose_name = _("self help") 12 | -------------------------------------------------------------------------------- /selfhelp/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /selfhelp/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import include, url,static 2 | import selfhelp.views 3 | 4 | urlpatterns = [ 5 | url(r"(?P\w+)/(?P\d+)/pay", 'selfhelp.views.pay_action'), 6 | ] 7 | -------------------------------------------------------------------------------- /selfhelp/views.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from django.contrib.admin import site 3 | from django.contrib.contenttypes.models import ContentType 4 | from django.db import connection 5 | from django.http.response import HttpResponseRedirect 6 | from django.utils.encoding import force_text 7 | from django.template.response import TemplateResponse 8 | from django.contrib import messages 9 | from django.utils.translation import ugettext_lazy as _ 10 | from django.contrib.contenttypes.models import ContentType 11 | 12 | 13 | def pay_action(request,model,object_id): 14 | title = _("Are you sure?") 15 | ct = ContentType.objects.get(app_label='selfhelp',model=model) 16 | obj = ct.get_object_for_this_type(id=int(object_id)) 17 | opts = obj._meta 18 | objects_name = force_text(opts.verbose_name) 19 | 20 | if model == 'reimbursement': 21 | loan = obj.loan 22 | amount = obj.logout_amount 23 | if loan and (amount is None or amount< 0): 24 | messages.error(request,u'您选择了借款单据,但是未正确填写核销金额,请在\'财务信息\'栏目中更正') 25 | return HttpResponseRedirect("/admin/selfhelp/%s/%s"%(model,object_id)) 26 | 27 | if request.POST.get("post"): 28 | try: 29 | obj.action_pay(request) 30 | messages.success(request,_('action successfully')) 31 | except Exception,e: 32 | messages.error(request,e) 33 | 34 | return HttpResponseRedirect("/admin/selfhelp/%s/%s"%(model,object_id)) 35 | 36 | context = dict( 37 | site.each_context(request), 38 | title=title, 39 | opts=opts, 40 | objects_name=objects_name, 41 | object=obj, 42 | action_name=_('pay') 43 | ) 44 | request.current_app = site.name 45 | 46 | return TemplateResponse(request,'admin/invent/stockin/in_confirmation.html', context) 47 | -------------------------------------------------------------------------------- /static/css/maximus.css: -------------------------------------------------------------------------------- 1 | 2 | table#workflow-history{ 3 | width:100% 4 | } 5 | table#workflow-history tbody th,table#workflow-history tbody td.col { 6 | width: 16em; 7 | } 8 | 9 | .submitlink { 10 | padding-left: 12px; 11 | background: url(../img/icon-yes.gif) 0 .25em no-repeat; 12 | } 13 | 14 | a.submitlink:link, a.submitlink:visited { 15 | color: #CC3434; 16 | } 17 | 18 | a.submitlink:hover { 19 | color: #993333; 20 | } 21 | 22 | label.control{ 23 | width:4em; 24 | } 25 | 26 | p.next-node{ 27 | font-weight:bold; 28 | } 29 | ul.node-users{ 30 | list-style:none; 31 | } 32 | p.tooltip{ 33 | color:#999; 34 | } 35 | li.done-link{ 36 | padding-left: 12px; 37 | background: url(../img/icon_yes.gif) 0 .2em no-repeat; 38 | } -------------------------------------------------------------------------------- /static/img/icon-submitlink.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/static/img/icon-submitlink.gif -------------------------------------------------------------------------------- /static/img/icon-submitlink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/static/img/icon-submitlink.png -------------------------------------------------------------------------------- /static/img/icon-yes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/static/img/icon-yes.gif -------------------------------------------------------------------------------- /static/js/maximus.js: -------------------------------------------------------------------------------- 1 | /** 2 | author:zhugl 3 | date:2015-05-15 4 | */ 5 | (function($) { 6 | $(document).ready(function() { 7 | /** 8 | set operation type 9 | */ 10 | $("fieldset.workflow input[type='radio']").on('click',function(){ 11 | operation = $(this).val(); 12 | next = "approve/"+operation; 13 | $("#workflow_approve").attr("href",next); 14 | }); 15 | try{ 16 | $("div.inline-group table tbody tr.form-row").removeClass('has_original'); 17 | $("div.inline-group table tbody tr.form-row td:first-child").find('p').hide(); 18 | }catch(e){ 19 | 20 | } 21 | }); 22 | })(django.jQuery); -------------------------------------------------------------------------------- /static/js/workorder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Administrator on 15-5-23. 3 | */ 4 | (function($) { 5 | $(document).ready(function() { 6 | /** 7 | set operation type 8 | */ 9 | $("#woextravalue_set-group select").attr('readonly','true') 10 | }); 11 | })(django.jQuery); 12 | -------------------------------------------------------------------------------- /syscfg/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | default_app_config = 'syscfg.apps.SysConfig' -------------------------------------------------------------------------------- /syscfg/admin.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from django.contrib import admin 3 | from django.forms import ModelForm,DateField 4 | from syscfg.models import * 5 | from common import generic 6 | 7 | 8 | class SiteForm(ModelForm): 9 | """ 10 | 11 | """ 12 | class Meta: 13 | model = Site 14 | fields = '__all__' 15 | 16 | 17 | class SiteAdmin(admin.ModelAdmin): 18 | list_per_page = 10 19 | list_display = ['name', 'begin', 'end'] 20 | fields = (('begin', 'end'), 'name', 'description', 'user') 21 | filter_horizontal = ['user'] 22 | form = SiteForm 23 | 24 | 25 | class ModuleAdmin(generic.BOAdmin): 26 | CODE_NUMBER_WIDTH = 3 27 | CODE_PREFIX = 'U' 28 | list_display = ['code','name','parent','status'] 29 | ordering = ['weight'] 30 | raw_id_fields = ['parent'] 31 | 32 | 33 | class MenuAdmin(generic.BOAdmin): 34 | CODE_NUMBER_WIDTH = 3 35 | CODE_PREFIX = 'M' 36 | 37 | list_display = ['code','name','module','status'] 38 | list_filter = ['module'] 39 | ordering = ['weight'] 40 | raw_id_fields = ['module'] 41 | 42 | 43 | class RoleAdmin(generic.BOAdmin): 44 | CODE_NUMBER_WIDTH = 3 45 | CODE_PREFIX = 'R' 46 | list_display = ['code','name','status'] 47 | filter_horizontal = ['users','menus'] 48 | 49 | 50 | admin.site.register(Site, SiteAdmin) 51 | admin.site.register(Module,ModuleAdmin) 52 | admin.site.register(Menu,MenuAdmin) 53 | admin.site.register(Role,RoleAdmin) 54 | -------------------------------------------------------------------------------- /syscfg/apps.py: -------------------------------------------------------------------------------- 1 | __author__ = 'zhugl' 2 | # created at 15-4-22 3 | from django.apps.config import AppConfig 4 | from django.utils.translation import ugettext_lazy as _ 5 | 6 | 7 | class SysConfig(AppConfig): 8 | name = 'syscfg' 9 | verbose_name = _('SysConfig') -------------------------------------------------------------------------------- /syscfg/models.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from django.db import models 3 | from django.contrib.auth.models import User 4 | from django.utils.translation import ugettext_lazy as _ 5 | from common import const 6 | from common import generic 7 | 8 | 9 | class Site(models.Model): 10 | """ 11 | 站点,一个站点下可有多个公司,处于同一个站点下的用户逻辑上位于同一个组织 12 | """ 13 | index_weight = 1 14 | begin = models.DateField(_('begin date'), blank=True,null=True) 15 | end = models.DateField(_('end date'), blank=True,null=True) 16 | name = models.CharField(_('site name'), max_length=const.DB_CHAR_NAME_40) 17 | description = models.TextField(_('site description'),blank=True,null=True) 18 | user = models.ManyToManyField(User,verbose_name=_('administrator')) 19 | 20 | def __unicode__(self): 21 | return u'%s'%self.name 22 | 23 | class Meta: 24 | verbose_name = _('Site') 25 | verbose_name_plural = _('Site') 26 | 27 | 28 | class Module(generic.BO): 29 | """ 30 | 模块管理 31 | """ 32 | index_weight = 2 33 | code = models.CharField(_("module code"),max_length=const.DB_CHAR_CODE_6,blank=True,null=True) 34 | name = models.CharField(_("module name"),max_length=const.DB_CHAR_NAME_40) 35 | url = models.URLField(_("module url"),blank=True,null=True,max_length=const.DB_CHAR_NAME_80) 36 | weight = models.IntegerField(_("weight"),blank=True,null=True,default=99) 37 | icon = models.CharField(_("style class"),blank=True,null=True,max_length=const.DB_CHAR_NAME_40) 38 | parent = models.ForeignKey('self',blank=True,null=True,verbose_name=_("parent")) 39 | status = models.BooleanField(_("in use"),default=True) 40 | 41 | class Meta: 42 | verbose_name = _("module") 43 | verbose_name_plural = _("module") 44 | 45 | 46 | class Menu(generic.BO): 47 | """ 48 | 菜单管理 49 | """ 50 | index_weight = 3 51 | module = models.ForeignKey(Module,verbose_name=_("module")) 52 | code = models.CharField(_("menu code"),max_length=const.DB_CHAR_CODE_6,blank=True,null=True) 53 | name = models.CharField(_("menu name"),max_length=const.DB_CHAR_NAME_40) 54 | url = models.URLField(_("menu url"),blank=True,null=True,max_length=const.DB_CHAR_NAME_80) 55 | weight = models.IntegerField(_("weight"),blank=True,null=True,default=99) 56 | icon = models.CharField(_("style class"),blank=True,null=True,max_length=const.DB_CHAR_NAME_40) 57 | status = models.BooleanField(_("in use"),default=True) 58 | 59 | class Meta: 60 | verbose_name = _("menu") 61 | verbose_name_plural = _("menu") 62 | 63 | 64 | class Role(generic.BO): 65 | """ 66 | 角色管理,分配用户所拥有的菜单 67 | """ 68 | index_weight = 4 69 | code = models.CharField(_("role code"),max_length=const.DB_CHAR_CODE_6,blank=True,null=True) 70 | name = models.CharField(_("role name"),max_length=const.DB_CHAR_NAME_40) 71 | description = models.CharField(_("description"),max_length=const.DB_CHAR_NAME_80,blank=True,null=True) 72 | status = models.BooleanField(_("in use"),default=True) 73 | parent = models.ForeignKey('self',blank=True,null=True,verbose_name=_("parent")) 74 | users = models.ManyToManyField(User,verbose_name=_("role users"),blank=True) 75 | menus = models.ManyToManyField(Menu,verbose_name=_("role menus"),blank=True) 76 | 77 | class Meta: 78 | verbose_name = _("role") 79 | verbose_name_plural = _("role") 80 | -------------------------------------------------------------------------------- /syscfg/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /syscfg/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /templates/admin/app_index.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/index.html" %} 2 | {% load i18n %} 3 | 4 | {% block bodyclass %}{{ block.super }} app-{{ app_label }}{% endblock %} 5 | 6 | {% if not is_popup %} 7 | {% block breadcrumbs %} 8 | 14 | {% endblock %} 15 | {% endif %} 16 | 17 | {% block sidebar %}{% endblock %} 18 | -------------------------------------------------------------------------------- /templates/admin/base.html: -------------------------------------------------------------------------------- 1 | {% load i18n admin_static %} 2 | {% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %} 3 | 4 | 5 | {% block title %}{% endblock %} 6 | 7 | {% block extrastyle %}{% endblock %} 8 | 9 | {% if LANGUAGE_BIDI %}{% endif %} 10 | 11 | 12 | {% block extrahead %}{% endblock %} 13 | {% block blockbots %}{% endblock %} 14 | 15 | {% load i18n %} 16 | 17 | 18 | 19 | 20 |
21 | 22 | {% if not is_popup %} 23 | 24 | 56 | 57 | {% block breadcrumbs %} 58 | 62 | {% endblock %} 63 | {% endif %} 64 | 65 | {% block messages %} 66 | {% if messages %} 67 |
    {% for message in messages %} 68 | {{ message|capfirst }} 69 | {% endfor %}
70 | {% endif %} 71 | {% endblock messages %} 72 | 73 | 74 |
75 | {% block pretitle %}{% endblock %} 76 | {% block content_title %}{% if title %}

{{ title }}

{% endif %}{% endblock %} 77 | {% block content %} 78 | {% block object-tools %}{% endblock %} 79 | {{ content }} 80 | {% endblock %} 81 | {% block sidebar %}{% endblock %} 82 |
83 |
84 | 85 | 86 | {% block footer %}{% endblock %} 87 |
88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /templates/admin/change_form.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% load i18n admin_urls admin_static admin_modify %} 3 | 4 | {% block extrahead %}{{ block.super }} 5 | 6 | {{ media }} 7 | {% endblock %} 8 | 9 | {% block extrastyle %}{{ block.super }}{% endblock %} 10 | 11 | {% block coltype %}colM{% endblock %} 12 | 13 | {% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-form{% endblock %} 14 | 15 | {% if not is_popup %} 16 | {% block breadcrumbs %} 17 | 23 | {% endblock %} 24 | {% endif %} 25 | 26 | {% block content %}
27 | {% block object-tools %} 28 | {% if change %}{% if not is_popup %} 29 |
    30 | {% block object-tools-items %} 31 |
  • 32 | {% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %} 33 | {% trans "History" %} 34 |
  • 35 | {% if has_absolute_url %}
  • {% trans "View on site" %}
  • {% endif %} 36 | {% endblock %} 37 |
38 | {% endif %}{% endif %} 39 | {% endblock %} 40 |
{% csrf_token %}{% block form_top %}{% endblock %} 41 |
42 | {% if is_popup %}{% endif %} 43 | {% if to_field %}{% endif %} 44 | {% if save_on_top %}{% block submit_buttons_top %}{% submit_row %}{% endblock %}{% endif %} 45 | {% if show_workflow_line%} 46 |
47 |

{% trans "workflow approve" %}

48 |
49 | {% if can_restart %} 50 | {% trans "restart workflow" %} 51 |

{% trans "your apply has been denied,you can restart a new apply" %}

52 | {% else %} 53 | 54 | 55 | 56 | {% trans "submit" %} 57 | {% endif %} 58 |
59 |
60 | {% endif %} 61 | {% if errors %} 62 |

63 | {% if errors|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %} 64 |

65 | {{ adminform.form.non_field_errors }} 66 | {% endif %} 67 | 68 | {% block field_sets %} 69 | {% for fieldset in adminform %} 70 | {% include "admin/includes/fieldset.html" %} 71 | {% endfor %} 72 | {% endblock %} 73 | 74 | {% block after_field_sets %}{% endblock %} 75 | 76 | {% block inline_field_sets %} 77 | {% for inline_admin_formset in inline_admin_formsets %} 78 | {% include inline_admin_formset.opts.template %} 79 | {% endfor %} 80 | {% endblock %} 81 | 82 | {% block after_related_objects %}{% endblock %} 83 | 84 | {% block submit_buttons_bottom %}{% submit_row %}{% endblock %} 85 | 86 | {% block admin_change_form_document_ready %} 87 | 141 | {% endblock %} 142 | 143 | {# JavaScript for prepopulated fields #} 144 | {% prepopulated_fields_js %} 145 | 146 |
147 |
148 | {% endblock %} 149 | -------------------------------------------------------------------------------- /templates/admin/index.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% load i18n admin_static %} 3 | 4 | {% block extrastyle %}{{ block.super }} 5 | 6 | {% endblock %} 7 | 8 | {% block coltype %}colMS{% endblock %} 9 | 10 | {% block bodyclass %}{{ block.super }} dashboard{% endblock %} 11 | 12 | {% block breadcrumbs %} 13 | 21 | {% endblock %} 22 | 23 | {% block content %} 24 |
25 | {% if maxi_app_list %} 26 | {% for app in maxi_app_list %} 27 | 28 |
29 | 30 | 33 | {% for model in app.models %} 34 | 35 | {% if model.admin_url %} 36 | 37 | {% else %} 38 | 39 | {% endif %} 40 | 41 | {% if model.add_url %} 42 | 43 | {% else %} 44 | 45 | {% endif %} 46 | 47 | {% if model.admin_url %} 48 | 49 | {% else %} 50 | 51 | {% endif %} 52 | 53 | {% endfor %} 54 | 55 |
31 | {{ app.name }} 32 |
{{ model.name }}{{ model.name }}{% trans 'Add' %} {% trans 'Change' %} 
56 |
57 | 58 | {% endfor %} 59 | {% else %} 60 |

{% trans "You don't have permission to edit anything." %}

61 | {% endif %} 62 |
63 | {% endblock %} 64 | 65 | {% block sidebar %} 66 | 110 | {% endblock %} 111 | -------------------------------------------------------------------------------- /templates/admin/invent/inventory/change_form.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% load i18n admin_urls admin_static admin_modify %} 3 | 4 | {% block extrahead %}{{ block.super }} 5 | 6 | {{ media }} 7 | {% endblock %} 8 | 9 | {% block extrastyle %}{{ block.super }}{% endblock %} 10 | 11 | {% block coltype %}colM{% endblock %} 12 | 13 | {% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-form{% endblock %} 14 | 15 | {% if not is_popup %} 16 | {% block breadcrumbs %} 17 | 23 | {% endblock %} 24 | {% endif %} 25 | 26 | {% block content %}
27 | {% block object-tools %} 28 | {% if change %}{% if not is_popup %} 29 |
    30 | {% block object-tools-items %} 31 |
  • 32 | {% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %} 33 | {% trans "History" %} 34 |
  • 35 | {% if has_absolute_url %}
  • {% trans "View on site" %}
  • {% endif %} 36 | {% endblock %} 37 |
38 | {% endif %}{% endif %} 39 | {% endblock %} 40 |
{% csrf_token %}{% block form_top %}{% endblock %} 41 |
42 | {% if is_popup %}{% endif %} 43 | {% if to_field %}{% endif %} 44 | {% if save_on_top %}{% block submit_buttons_top %}{% submit_row %}{% endblock %}{% endif %} 45 | {% if show_workflow_line%} 46 |
47 |

{% trans "workflow approve" %}

48 |
49 | {% if can_restart %} 50 | {% trans "restart workflow" %} 51 |

{% trans "your apply has been denied,you can restart a new apply" %}

52 | {% else %} 53 | 54 | 55 | 56 | {% trans "submit" %} 57 | {% endif %} 58 |
59 |
60 | {% endif %} 61 | {% if errors %} 62 |

63 | {% if errors|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %} 64 |

65 | {{ adminform.form.non_field_errors }} 66 | {% endif %} 67 | 68 | {% block field_sets %} 69 | {% for fieldset in adminform %} 70 | {% include "admin/includes/fieldset.html" %} 71 | {% endfor %} 72 | {% endblock %} 73 | 74 | {% block after_field_sets %}{% endblock %} 75 | 76 | {% block inline_field_sets %} 77 | {% for inline_admin_formset in inline_admin_formsets %} 78 | {% include inline_admin_formset.opts.template %} 79 | {% endfor %} 80 | {% endblock %} 81 | 82 | {% block after_related_objects %}{% endblock %} 83 | 84 | {% block submit_buttons_bottom %}{% submit_row %}{% endblock %} 85 | 86 | {% block admin_change_form_document_ready %} 87 | 128 | {% endblock %} 129 | 130 | {# JavaScript for prepopulated fields #} 131 | {% prepopulated_fields_js %} 132 | 133 |
134 |
135 | {% if detail %} 136 |

{% trans "InOut History" %}

137 |
138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | {% for item in detail %} 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | {% endfor %} 162 | 163 | 164 |
{% trans 'execute time' %}{% trans 'plus or minus prop' %}{% trans 'price' %}{% trans 'count' %}{% trans 'measure' %}{% trans 'status' %}{% trans 'source' %}
{{ item.event_time|date:"DATETIME_FORMAT" }}{{ item.prop }}{{ item.price }}{{ item.cnt }}{{ item.measure }}{% if item.status %} {% trans 'EXECUTED'%} {% endif %}{{ item.source }}
165 |
166 | {% endif %} 167 | {% endblock %} 168 | -------------------------------------------------------------------------------- /templates/admin/invent/stockin/change_form.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% load i18n admin_urls admin_static admin_modify %} 3 | 4 | {% block extrahead %}{{ block.super }} 5 | 6 | {{ media }} 7 | {% endblock %} 8 | 9 | {% block extrastyle %}{{ block.super }}{% endblock %} 10 | 11 | {% block coltype %}colM{% endblock %} 12 | 13 | {% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-form{% endblock %} 14 | 15 | {% if not is_popup %} 16 | {% block breadcrumbs %} 17 | 23 | {% endblock %} 24 | {% endif %} 25 | 26 | {% block content %}
27 | {% block object-tools %} 28 | {% if change %}{% if not is_popup %} 29 |
    30 | {% block object-tools-items %} 31 |
  • 32 | {% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %} 33 | {% trans "History" %} 34 |
  • 35 | {% if has_absolute_url %}
  • {% trans "View on site" %}
  • {% endif %} 36 | {% endblock %} 37 |
38 | {% endif %}{% endif %} 39 | {% endblock %} 40 |
{% csrf_token %}{% block form_top %}{% endblock %} 41 |
42 | {% if is_popup %}{% endif %} 43 | {% if to_field %}{% endif %} 44 | {% if save_on_top %}{% block submit_buttons_top %}{% submit_row %}{% endblock %}{% endif %} 45 | {% if show_workflow_line%} 46 |
47 |

{% trans "workflow approve" %}

48 |
49 | {% if can_restart %} 50 | {% trans "restart workflow" %} 51 |

{% trans "your apply has been denied,you can restart a new apply" %}

52 | {% else %} 53 | 54 | 55 | 56 | {% trans "submit" %} 57 | {% endif %} 58 |
59 |
60 | {% endif %} 61 | {% if errors %} 62 |

63 | {% if errors|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %} 64 |

65 | {{ adminform.form.non_field_errors }} 66 | {% endif %} 67 | 68 | {% block field_sets %} 69 | {% for fieldset in adminform %} 70 | {% include "admin/includes/fieldset.html" %} 71 | {% endfor %} 72 | {% endblock %} 73 | 74 | {% block after_field_sets %}{% endblock %} 75 | 76 | {% block inline_field_sets %} 77 | {% for inline_admin_formset in inline_admin_formsets %} 78 | {% include inline_admin_formset.opts.template %} 79 | {% endfor %} 80 | {% endblock %} 81 | 82 | {% block after_related_objects %}{% endblock %} 83 | 84 | {% block submit_buttons_bottom %}{% submit_row %}{% endblock %} 85 | 86 | {% block admin_change_form_document_ready %} 87 | 129 | {% endblock %} 130 | 131 | {# JavaScript for prepopulated fields #} 132 | {% prepopulated_fields_js %} 133 | 134 |
135 |
136 | {% endblock %} 137 | -------------------------------------------------------------------------------- /templates/admin/invent/stockin/in_confirmation.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% load i18n admin_urls %} 3 | 4 | {% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation{% endblock %} 5 | 6 | {% block breadcrumbs %} 7 | 14 | {% endblock %} 15 | 16 | {% block content %} 17 |

{% trans 'Are your sure to execute the operations?' %}

18 |
{% csrf_token %} 19 |
20 | 21 | {% if is_popup %}{% endif %} 22 | {% if to_field %}{% endif %} 23 | 24 | {% trans "No, take me back" %} 25 |
26 |
27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /templates/admin/invent/stockout/change_form.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% load i18n admin_urls admin_static admin_modify %} 3 | 4 | {% block extrahead %}{{ block.super }} 5 | 6 | {{ media }} 7 | {% endblock %} 8 | 9 | {% block extrastyle %}{{ block.super }}{% endblock %} 10 | 11 | {% block coltype %}colM{% endblock %} 12 | 13 | {% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-form{% endblock %} 14 | 15 | {% if not is_popup %} 16 | {% block breadcrumbs %} 17 | 23 | {% endblock %} 24 | {% endif %} 25 | 26 | {% block content %}
27 | {% block object-tools %} 28 | {% if change %}{% if not is_popup %} 29 |
    30 | {% block object-tools-items %} 31 |
  • 32 | {% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %} 33 | {% trans "History" %} 34 |
  • 35 | {% if has_absolute_url %}
  • {% trans "View on site" %}
  • {% endif %} 36 | {% endblock %} 37 |
38 | {% endif %}{% endif %} 39 | {% endblock %} 40 |
{% csrf_token %}{% block form_top %}{% endblock %} 41 |
42 | {% if is_popup %}{% endif %} 43 | {% if to_field %}{% endif %} 44 | {% if save_on_top %}{% block submit_buttons_top %}{% submit_row %}{% endblock %}{% endif %} 45 | {% if show_workflow_line%} 46 |
47 |

{% trans "workflow approve" %}

48 |
49 | {% if can_restart %} 50 | {% trans "restart workflow" %} 51 |

{% trans "your apply has been denied,you can restart a new apply" %}

52 | {% else %} 53 | 54 | 55 | 56 | {% trans "submit" %} 57 | {% endif %} 58 |
59 |
60 | {% endif %} 61 | {% if errors %} 62 |

63 | {% if errors|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %} 64 |

65 | {{ adminform.form.non_field_errors }} 66 | {% endif %} 67 | 68 | {% block field_sets %} 69 | {% for fieldset in adminform %} 70 | {% include "admin/includes/fieldset.html" %} 71 | {% endfor %} 72 | {% endblock %} 73 | 74 | {% block after_field_sets %}{% endblock %} 75 | 76 | {% block inline_field_sets %} 77 | {% for inline_admin_formset in inline_admin_formsets %} 78 | {% include inline_admin_formset.opts.template %} 79 | {% endfor %} 80 | {% endblock %} 81 | 82 | {% block after_related_objects %}{% endblock %} 83 | 84 | {% block submit_buttons_bottom %}{% submit_row %}{% endblock %} 85 | 86 | {% block admin_change_form_document_ready %} 87 | 130 | {% endblock %} 131 | 132 | {# JavaScript for prepopulated fields #} 133 | {% prepopulated_fields_js %} 134 | 135 |
136 |
137 | {% endblock %} 138 | -------------------------------------------------------------------------------- /templates/admin/invent/stockout/out_confirmation.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% load i18n admin_urls %} 3 | 4 | {% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation{% endblock %} 5 | 6 | {% block breadcrumbs %} 7 | 14 | {% endblock %} 15 | 16 | {% block content %} 17 |

{% trans 'Are your sure to execute the check out operations?' %}

18 |
{% csrf_token %} 19 |
20 | 21 | {% if is_popup %}{% endif %} 22 | {% if to_field %}{% endif %} 23 | 24 | {% trans "No, take me back" %} 25 |
26 |
27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /templates/admin/object_history.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% load i18n admin_urls %} 3 | {% block extrastyle %} 4 | 5 | {% endblock %} 6 | {% block breadcrumbs %} 7 | 14 | {% endblock %} 15 | 16 | {% block content %} 17 |
18 |
19 | 20 | {% if action_list %} 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {% for action in action_list %} 31 | 32 | 33 | 34 | 35 | 36 | {% endfor %} 37 | 38 |
{% trans 'Date/time' %}{% trans 'User' %}{% trans 'Action' %}
{{ action.action_time|date:"DATETIME_FORMAT" }}{{ action.user.get_username }}{% if action.user.get_full_name %} ({{ action.user.get_full_name }}){% endif %}{{ action.change_message }}
39 | {% else %} 40 |

{% trans "This object doesn't have a change history. It probably wasn't added via this admin site." %}

41 | {% endif %} 42 |
43 | {% if history_list %} 44 |

{% trans "Workflow History" %}

45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | {% for history in history_list %} 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | {% endfor %} 66 | {% if todo_list %} 67 | {% for todo in todo_list %} 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | {% endfor %} 76 | {% endif %} 77 | 78 |
{% trans 'Date/time' %}{% trans 'User' %}{% trans 'node' %}{% trans 'Action' %}{% trans 'Workflow Memo' %}
{{ history.pro_time|date:"DATETIME_FORMAT" }}{{ history.user.get_username }}{% if history.user.get_full_name %} ({{ history.user.get_full_name }}){% endif %}{{ history.get_node_desc }}{{ history.get_action_desc }}{{ history.get_memo_desc }}
{% if todo.is_read %}{{ todo.read_time|date:"DATETIME_FORMAT" }}{% endif %}{{ todo.user.get_username }}{% if todo.user.get_full_name %} ({{ todo.user.get_full_name }}){% endif %}{{ todo.node.name }}{% if todo.is_read %} {% trans 'already read' %} {% else %} {% trans 'unread' %} {% endif %}
79 |
80 | {% endif %} 81 |
82 | {% endblock %} 83 | -------------------------------------------------------------------------------- /templates/admin/selfhelp/workorder/change_form.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% load i18n admin_urls admin_static admin_modify %} 3 | 4 | {% block extrahead %}{{ block.super }} 5 | 6 | {{ media }} 7 | {% endblock %} 8 | 9 | {% block extrastyle %}{{ block.super }}{% endblock %} 10 | 11 | {% block coltype %}colM{% endblock %} 12 | 13 | {% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-form{% endblock %} 14 | 15 | {% if not is_popup %} 16 | {% block breadcrumbs %} 17 | 23 | {% endblock %} 24 | {% endif %} 25 | 26 | {% block content %}
27 | {% block object-tools %} 28 | {% if change %}{% if not is_popup %} 29 |
    30 | {% block object-tools-items %} 31 |
  • 32 | {% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %} 33 | {% trans "History" %} 34 |
  • 35 | {% if has_absolute_url %}
  • {% trans "View on site" %}
  • {% endif %} 36 | {% endblock %} 37 |
38 | {% endif %}{% endif %} 39 | {% endblock %} 40 |
{% csrf_token %}{% block form_top %}{% endblock %} 41 |
42 | {% if is_popup %}{% endif %} 43 | {% if to_field %}{% endif %} 44 | {% if save_on_top %}{% block submit_buttons_top %}{% submit_row %}{% endblock %}{% endif %} 45 | {% if show_workflow_line%} 46 |
47 |

{% trans "workflow approve" %}

48 |
49 | {% if can_restart %} 50 | {% trans "restart workflow" %} 51 |

{% trans "your apply has been denied,you can restart a new apply" %}

52 | {% else %} 53 | 54 | 55 | 56 | {% trans "submit" %} 57 | {% endif %} 58 |
59 |
60 | {% endif %} 61 | {% if errors %} 62 |

63 | {% if errors|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %} 64 |

65 | {{ adminform.form.non_field_errors }} 66 | {% endif %} 67 | 68 | {% block field_sets %} 69 | {% for fieldset in adminform %} 70 | {% include "admin/includes/fieldset.html" %} 71 | {% endfor %} 72 | {% endblock %} 73 | 74 | {% block after_field_sets %}{% endblock %} 75 | 76 | {% block inline_field_sets %} 77 | {% for inline_admin_formset in inline_admin_formsets %} 78 | {% include inline_admin_formset.opts.template %} 79 | {% endfor %} 80 | {% endblock %} 81 | 82 | {% block after_related_objects %}{% endblock %} 83 | 84 | {% block submit_buttons_bottom %}{% submit_row %}{% endblock %} 85 | 86 | {% block admin_change_form_document_ready %} 87 | 166 | {% endblock %} 167 | 168 | {# JavaScript for prepopulated fields #} 169 | {% prepopulated_fields_js %} 170 | 171 |
172 |
173 | {% endblock %} 174 | -------------------------------------------------------------------------------- /templates/admin/submit_line.html: -------------------------------------------------------------------------------- 1 | {% load i18n admin_urls %} 2 |
3 | 4 | {% if show_save %}{% endif %} 5 | {% trans "submit" %} 6 | {% for button in extra_buttons %} 7 | {{button.title}} 8 | {% endfor %} 9 | {{extra_buttons}} 10 | {% if show_delete_link %} 11 | {% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %} 12 | 14 | {% endif %} 15 | {% if show_save_as_new %}{% endif %} 16 | {% if show_save_and_add_another %}{% endif %} 17 | {% if show_save_and_continue %}{% endif %} 18 |
19 | -------------------------------------------------------------------------------- /templates/admin/workflow/node/change_form.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% load i18n admin_urls admin_static admin_modify %} 3 | 4 | {% block extrahead %}{{ block.super }} 5 | 6 | {{ media }} 7 | {% endblock %} 8 | 9 | {% block extrastyle %}{{ block.super }}{% endblock %} 10 | 11 | {% block coltype %}colM{% endblock %} 12 | 13 | {% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-form{% endblock %} 14 | 15 | {% if not is_popup %} 16 | {% block breadcrumbs %} 17 | 23 | {% endblock %} 24 | {% endif %} 25 | 26 | {% block content %}
27 | {% block object-tools %} 28 | {% if change %}{% if not is_popup %} 29 |
    30 | {% block object-tools-items %} 31 |
  • 32 | {% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %} 33 | {% trans "History" %} 34 |
  • 35 | {% if has_absolute_url %}
  • {% trans "View on site" %}
  • {% endif %} 36 | {% endblock %} 37 |
38 | {% endif %}{% endif %} 39 | {% endblock %} 40 |
{% csrf_token %}{% block form_top %}{% endblock %} 41 |
42 | {% if is_popup %}{% endif %} 43 | {% if to_field %}{% endif %} 44 | {% if save_on_top %}{% block submit_buttons_top %}{% submit_row %}{% endblock %}{% endif %} 45 | {% if show_workflow_line%} 46 |
47 |

{% trans "workflow approve" %}

48 |
49 | {% if can_restart %} 50 | {% trans "restart workflow" %} 51 |

{% trans "your apply has been denied,you can restart a new apply" %}

52 | {% else %} 53 | 54 | 55 | 56 | {% trans "submit" %} 57 | {% endif %} 58 |
59 |
60 | {% endif %} 61 | {% if errors %} 62 |

63 | {% if errors|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %} 64 |

65 | {{ adminform.form.non_field_errors }} 66 | {% endif %} 67 | 68 | {% block field_sets %} 69 | {% for fieldset in adminform %} 70 | {% include "admin/includes/fieldset.html" %} 71 | {% endfor %} 72 | {% endblock %} 73 | 74 | {% block after_field_sets %}{% endblock %} 75 | 76 | {% block inline_field_sets %} 77 | {% for inline_admin_formset in inline_admin_formsets %} 78 | {% include inline_admin_formset.opts.template %} 79 | {% endfor %} 80 | {% endblock %} 81 | 82 | {% block after_related_objects %}{% endblock %} 83 | 84 | {% block submit_buttons_bottom %}{% submit_row %}{% endblock %} 85 | 86 | {% block admin_change_form_document_ready %} 87 | 157 | {% endblock %} 158 | 159 | {# JavaScript for prepopulated fields #} 160 | {% prepopulated_fields_js %} 161 | 162 |
163 |
164 | {% endblock %} 165 | -------------------------------------------------------------------------------- /templates/default/workflow/workflow_approve_confirmation.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% load i18n admin_urls %} 3 | {% block extrastyle %}{{ block.super }}{% endblock %} 4 | {% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation{% endblock %} 5 | 6 | {% block breadcrumbs %} 7 | 14 | {% endblock %} 15 | 16 | {% block content %} 17 | {% if operation == '4' %} 18 |

{% trans "Are you sure to terminate the request?" %}

19 | {% elif operation == '3' %} 20 |

{% trans "Are you sure to deny the request?" %}

21 | {% else %} 22 |

{% blocktrans with escaped_object=object %}Are you sure you want to submit the {{ object_name }} "{{ escaped_object }}"? {% endblocktrans %}

23 | {% endif%} 24 |

{{ workflow_modal.code }} {{ workflow_modal.name }}

25 | 26 |
{% csrf_token %} 27 | {% if is_stop_node %} 28 |

{% trans "current node is stop node,click the submit button to complete it" %}

29 | {% else %} 30 | {% for node_user in node_users %} 31 | {% if node_user.node == 'start'%} 32 |

{% trans "back to start node" %}

33 | {%else%} 34 |

{% trans "next node" %}:{{ node_user.node.name }}

35 | {% endif%} 36 | {% if next_node_description %} 37 |

{% trans "attention" %}:{{next_node_description}}

38 | {% endif %} 39 | {% if node_has_users %} 40 |
    41 | {% for user in node_user.users %} 42 |
  • {{user.last_name}}{{user.first_name}}
  • 43 | {% endfor %} 44 |
45 | {% else %} 46 |

{% trans "No user was configured to handle this node"%}

47 | {% endif %} 48 | {% endfor%} 49 | {% endif%} 50 | {% if is_stop_node or node_has_users%} 51 | 52 | 53 |
54 | 55 | 56 | {% if is_popup %}{% endif %} 57 | {% if to_field %}{% endif %} 58 | 59 | {% trans "No, take me back" %} 60 |
61 | {% else %} 62 | {% if is_popup %}{% endif %} 63 | {% if to_field %}{% endif %} 64 | {% trans "No, take me back" %} 65 | {% endif %} 66 |
67 | {% endblock %} 68 | -------------------------------------------------------------------------------- /templates/default/workflow/workflow_start_confirmation.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base_site.html" %} 2 | {% load i18n admin_urls %} 3 | 4 | {% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation{% endblock %} 5 | 6 | {% block breadcrumbs %} 7 | 14 | {% endblock %} 15 | 16 | {% block content %} 17 | {% if has_workflow %} 18 |

{% blocktrans with escaped_object=object %}Are you sure you want to submit the {{ object_name }} "{{ escaped_object }}"? {% endblocktrans %}

19 |

{{ workflow_modal.code }} {{ workflow_modal.name }}

20 | 21 |
{% csrf_token %} 22 | {% if next_node %} 23 |

{% trans "next node" %}:{{ next_node.name }}

24 | {% if has_next_user %} 25 |
    26 | {% for user in next_users %} 27 |
  • {{user.last_name}}{{user.first_name}}
  • 28 | {% endfor %} 29 |
30 | {% else %} 31 |

{% trans "No user was configured to handle this node"%}

32 | {% endif %} 33 | {% endif %} 34 |
35 | 36 | {% if is_popup %}{% endif %} 37 | {% if to_field %}{% endif %} 38 | 39 | {% trans "No, take me back" %} 40 |
41 |
42 | {% else %} 43 |

{% trans "you needs to config a workflow model for this content type" %}

44 | {% trans "OK" %} 45 | {% endif %} 46 | {% endblock %} 47 | -------------------------------------------------------------------------------- /upload/data/1001部门岗位员工导入模板test.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/data/1001部门岗位员工导入模板test.xls -------------------------------------------------------------------------------- /upload/data/1003合作伙伴导入模板.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/data/1003合作伙伴导入模板.xls -------------------------------------------------------------------------------- /upload/data/1003合作伙伴导入模板_ENnUPsg.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/data/1003合作伙伴导入模板_ENnUPsg.xls -------------------------------------------------------------------------------- /upload/data/1004项目导入模板.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/data/1004项目导入模板.xls -------------------------------------------------------------------------------- /upload/data/1004项目导入模板_MDj3PCr.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/data/1004项目导入模板_MDj3PCr.xls -------------------------------------------------------------------------------- /upload/data/1004项目导入模板_QE5NXrr.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/data/1004项目导入模板_QE5NXrr.xls -------------------------------------------------------------------------------- /upload/data/1004项目导入模板_Sfco4Ck.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/data/1004项目导入模板_Sfco4Ck.xls -------------------------------------------------------------------------------- /upload/data/1004项目导入模板_YfVeRXU.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/data/1004项目导入模板_YfVeRXU.xls -------------------------------------------------------------------------------- /upload/data/1005用户信息导入模板.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/data/1005用户信息导入模板.xls -------------------------------------------------------------------------------- /upload/data/1005用户信息导入模板_LRE2NKU.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/data/1005用户信息导入模板_LRE2NKU.xls -------------------------------------------------------------------------------- /upload/data/1005用户信息导入模板_stkXmtR.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/data/1005用户信息导入模板_stkXmtR.xls -------------------------------------------------------------------------------- /upload/data/合作伙伴.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/data/合作伙伴.xls -------------------------------------------------------------------------------- /upload/doc/1001部门岗位员工导入模板.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/doc/1001部门岗位员工导入模板.xls -------------------------------------------------------------------------------- /upload/doc/1002期初库存导入模板.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/doc/1002期初库存导入模板.csv -------------------------------------------------------------------------------- /upload/doc/1003合作伙伴导入模板.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/doc/1003合作伙伴导入模板.xls -------------------------------------------------------------------------------- /upload/doc/1004项目导入模板.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/doc/1004项目导入模板.xls -------------------------------------------------------------------------------- /upload/doc/2001报价单明细样例.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/doc/2001报价单明细样例.xls -------------------------------------------------------------------------------- /upload/doc/2002需求计划明细样例.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/doc/2002需求计划明细样例.xls -------------------------------------------------------------------------------- /upload/doc/2003采购单明细样例.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/doc/2003采购单明细样例.xls -------------------------------------------------------------------------------- /upload/inventory/INVENTORY.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/inventory/INVENTORY.csv -------------------------------------------------------------------------------- /upload/inventory/INVENTORY_PPfDioe.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/inventory/INVENTORY_PPfDioe.csv -------------------------------------------------------------------------------- /upload/project/2014年17月份时事政治全集.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/project/2014年17月份时事政治全集.docx -------------------------------------------------------------------------------- /upload/project/综合知识与能力素质.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/upload/project/综合知识与能力素质.docx -------------------------------------------------------------------------------- /workflow/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'workflow.apps.WorkflowConfig' -------------------------------------------------------------------------------- /workflow/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.contrib.contenttypes.models import ContentType 3 | from common import generic 4 | from workflow.models import Modal,Node,TodoList,Instance,History 5 | 6 | 7 | class NodeInline(admin.TabularInline): 8 | model = Node 9 | fields = ['code','name','next_user_handler','can_deny','can_terminate'] 10 | 11 | def get_extra(self, request, obj=None, **kwargs): 12 | if obj: 13 | return 0 14 | else: 15 | return 1 16 | 17 | 18 | class WorkflowModelAdmin(admin.ModelAdmin): 19 | list_display = ['code','name','begin','end'] 20 | inlines = [NodeInline] 21 | readonly_fields = ['app_name','model_name'] 22 | raw_id_fields = ['content_type'] 23 | fields = ( 24 | ('begin','end',),('code','name',),('description',),('content_type',),('app_name','model_name',), 25 | ) 26 | 27 | def get_form(self, request, obj=None, **kwargs): 28 | print kwargs 29 | return super(WorkflowModelAdmin,self).get_form(request,obj,**kwargs) 30 | 31 | def save_model(self, request, obj, form, change): 32 | import datetime 33 | super(WorkflowModelAdmin,self).save_model(request,obj,form,change) 34 | 35 | if not obj.code: 36 | code = 'WF%03d' % obj.id 37 | obj.code = code 38 | obj.save() 39 | if not obj.begin: 40 | obj.begin = datetime.date.today() 41 | obj.end = datetime.date(9999,12,31) 42 | app_name = obj.content_type.app_label 43 | model_name = obj.content_type.model 44 | obj.app_name = app_name 45 | obj.model_name = model_name 46 | obj.save() 47 | 48 | 49 | class WorkflowNodeAdmin(admin.ModelAdmin): 50 | fields = ( 51 | ('modal',), 52 | ('name','code',), 53 | ('start','stop','can_terminate','can_deny','can_edit',), 54 | ('email_notice','short_message_notice','approve_node',),('next',), 55 | ('handler_type',), 56 | ('handler',), 57 | ('next_user_handler','next_node_handler',), 58 | ('status_field','status_value',),('action',), 59 | ('users',),('positions',),('roles',), 60 | ) 61 | list_display = ['code','name','modal','can_deny','can_terminate'] 62 | list_display_links = ['code','name'] 63 | list_filter = ['modal'] 64 | readonly_fields = ['modal','code'] 65 | filter_horizontal = ['next','users','positions','roles','departments',] 66 | radio_fields = {'handler_type':admin.HORIZONTAL} 67 | search_fields = ['modal__code','modal__name','code','name'] 68 | 69 | def get_readonly_fields(self, request, obj=None): 70 | if obj and obj.stop: 71 | return ['modal','code','next'] 72 | return super(WorkflowNodeAdmin,self).get_readonly_fields(request,obj) 73 | 74 | def formfield_for_manytomany(self, db_field, request=None, **kwargs): 75 | if db_field.name == 'next': 76 | apps = generic.get_app_model_info_from_request(request) 77 | # print apps 78 | if apps and apps.get('obj'): 79 | # print 'it is here' 80 | obj = apps.get('obj') 81 | # print obj.modal 82 | kwargs['queryset'] = Node.objects.filter(modal=obj.modal).exclude(id=obj.id) 83 | else: 84 | kwargs['queryset'] = Node.objects.filter(id=-1) 85 | 86 | return super(WorkflowNodeAdmin,self).formfield_for_manytomany(db_field,request,**kwargs) 87 | 88 | 89 | class InstanceAdmin(admin.ModelAdmin): 90 | list_display = ['code','modal','starter','start_time','status'] 91 | readonly_fields = ['code','modal','starter','start_time','status','object_id','approved_time','current_nodes'] 92 | 93 | 94 | class TodoAdmin(admin.ModelAdmin): 95 | list_display = ['code_link','modal_dsc','href','node_dsc','is_read','status','submitter','arrived_time'] 96 | list_filter = ['status'] 97 | 98 | def get_queryset(self, request): 99 | return super(TodoAdmin,self).get_queryset(request).filter(user=request.user) 100 | 101 | 102 | class HistoryAdmin(admin.ModelAdmin): 103 | list_display = ['inst','node','user','pro_time','memo'] 104 | 105 | 106 | class ContentTypeAdmin(admin.ModelAdmin): 107 | list_display = ['app_label','model'] 108 | search_fields = ['app_label','model'] 109 | list_per_page = 20 110 | list_filter = ['app_label'] 111 | 112 | 113 | admin.site.register(Modal, WorkflowModelAdmin) 114 | admin.site.register(Node, WorkflowNodeAdmin) 115 | admin.site.register(Instance, InstanceAdmin) 116 | admin.site.register(TodoList, TodoAdmin) 117 | admin.site.register(History, HistoryAdmin) 118 | admin.site.register(ContentType,ContentTypeAdmin) 119 | -------------------------------------------------------------------------------- /workflow/apps.py: -------------------------------------------------------------------------------- 1 | __author__ = 'zhugl' 2 | # created at 15-4-22 3 | from django.apps.config import AppConfig 4 | from django.utils.translation import ugettext_lazy as _ 5 | 6 | 7 | class WorkflowConfig(AppConfig): 8 | name = 'workflow' 9 | verbose_name = _('workflow') -------------------------------------------------------------------------------- /workflow/models.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from django.db import models 3 | from django.contrib.contenttypes.models import ContentType 4 | from django.contrib.auth.models import User 5 | from django.utils.html import format_html 6 | from django.utils.translation import ugettext_lazy as _ 7 | from common import const 8 | from syscfg.models import Role 9 | from organ.models import Position,OrgUnit 10 | 11 | 12 | class Modal(models.Model): 13 | """ 14 | 15 | """ 16 | import datetime 17 | index_weight = 1 18 | code = models.CharField(_("workflow code"),max_length=const.DB_CHAR_CODE_6,blank=True,null=True) 19 | name = models.CharField(_("workflow name"),max_length=const.DB_CHAR_NAME_40) 20 | description = models.TextField(_("description"),blank=True,null=True) 21 | content_type = models.ForeignKey(ContentType,verbose_name=_("content type"),limit_choices_to={"app_label__in":['basedata','organ']}) 22 | app_name = models.CharField(_("app name"),max_length=const.DB_CHAR_NAME_60,blank=True,null=True) 23 | model_name = models.CharField(_("model name"),max_length=const.DB_CHAR_NAME_60,blank=True,null=True) 24 | # added by zhugl 2015-05-10 25 | begin = models.DateField(_("begin date"),blank=True,null=True,default=datetime.date.today) 26 | end = models.DateField(_("end date"),blank=True,null=True,default=datetime.date(9999,12,31)) 27 | 28 | def __unicode__(self): 29 | return "%s" % self.name 30 | 31 | class Meta: 32 | verbose_name = _("workflow model") 33 | verbose_name_plural = _("workflow model") 34 | 35 | 36 | class Node(models.Model): 37 | """ 38 | submitter() 39 | upper() 40 | user() 41 | role() 42 | position() 43 | sql() 44 | etc:upper('zhangsan','lisi') 45 | """ 46 | HANDLER_TYPE = ( 47 | (1, _("designated user")), 48 | (2, _("designated position")), 49 | (3, _("designated role")), 50 | (4, _("submitter")), 51 | ) 52 | index_weight = 2 53 | modal = models.ForeignKey(Modal,verbose_name=_("workflow model")) 54 | code = models.CharField(_("node code"),max_length=const.DB_CHAR_CODE_4,blank=True,null=True) 55 | name = models.CharField(_("node name"),max_length=const.DB_CHAR_NAME_80) 56 | tooltip = models.CharField(_('tooltip words'),blank=True,null=True,max_length=const.DB_CHAR_NAME_120) 57 | 58 | start = models.BooleanField(_("start node"),default=False) 59 | stop = models.BooleanField(_("stop node"),default=False) 60 | can_terminate = models.BooleanField(_("can terminate"),default=False) 61 | can_deny = models.BooleanField(_("can deny"),default=True) 62 | can_edit = models.BooleanField(_("can edit"),default=False) 63 | 64 | email_notice = models.BooleanField(_("email notice"),default=True) 65 | short_message_notice = models.BooleanField(_("short message notice"),default=False) 66 | approve_node = models.BooleanField(_("approve node"),default=False) 67 | handler = models.TextField(_("handler"),blank=True,null=True,help_text=u'自定义SQL语句,优先高于指定用户、岗位、角色') 68 | # added by zhugl 2015-05-10 69 | handler_type = models.IntegerField(_("handler type"),choices=HANDLER_TYPE,default=1) 70 | positions = models.ManyToManyField(Position,verbose_name=_("designated position"),blank=True) 71 | roles = models.ManyToManyField(Role,verbose_name=_("designated role"),blank=True) 72 | users = models.ManyToManyField(User,verbose_name=_("designated user"),blank=True) 73 | departments = models.ManyToManyField(OrgUnit,verbose_name=_("designated department"),blank=True) 74 | next = models.ManyToManyField('self',blank=True,verbose_name=_("next node"),symmetrical=False) 75 | # added by zhugl 2015-06-30 76 | next_user_handler = models.CharField(_('next user handler'),blank=True,null=True,max_length=const.DB_CHAR_NAME_40) 77 | next_node_handler = models.CharField(_('next node handler'),blank=True,null=True,max_length=const.DB_CHAR_NAME_40) 78 | status_field = models.CharField(_('status field'),blank=True,null=True,max_length=const.DB_CHAR_NAME_40) 79 | status_value = models.CharField(_('status value'),blank=True,null=True,max_length=const.DB_CHAR_NAME_40) 80 | action = models.CharField(_('execute action'),blank=True,null=True,max_length=const.DB_CHAR_NAME_40) 81 | 82 | def save(self, force_insert=False, force_update=False, using=None, 83 | update_fields=None): 84 | if not self.code: 85 | fmt = 'N%02d' 86 | self.code = fmt % (self.modal.node_set.count()+1) 87 | super(Node,self).save(force_insert,force_update,using,update_fields) 88 | 89 | def __unicode__(self): 90 | return "%s-%s" % (self.code, self.name) 91 | 92 | class Meta: 93 | verbose_name = _("workflow node") 94 | verbose_name_plural = _("workflow node") 95 | 96 | 97 | class Instance(models.Model): 98 | """ 99 | 100 | """ 101 | STATUS = ( 102 | (1, _("NEW")), 103 | (2, _("IN PROGRESS")), 104 | (3, _("DENY")), 105 | (4, _("TERMINATED")), 106 | (9, _("APPROVED")), 107 | (99, _("COMPLETED")) 108 | ) 109 | index_weight = 3 110 | code = models.CharField(_("code"),blank=True,null=True,max_length=const.DB_CHAR_CODE_10) 111 | modal = models.ForeignKey(Modal,verbose_name=_("workflow model")) 112 | object_id = models.PositiveIntegerField("object id") 113 | starter = models.ForeignKey(User,verbose_name=_("start user"),related_name="starter") 114 | start_time = models.DateTimeField(_("start time"),auto_now_add=True) 115 | approved_time = models.DateTimeField(_("approved time"),blank=True,null=True) 116 | status = models.IntegerField(_("status"),default=1,choices=STATUS) 117 | current_nodes = models.ManyToManyField(Node,verbose_name=_("current node"),blank=True) 118 | 119 | def __unicode__(self): 120 | return '%s' % self.code 121 | 122 | def save(self, force_insert=False, force_update=False, using=None, 123 | update_fields=None): 124 | super(Instance,self).save(force_insert,force_update,using,update_fields) 125 | if not self.code: 126 | self.code = 'S%05d'%self.id 127 | self.save() 128 | 129 | class Meta: 130 | verbose_name = _("workflow instance") 131 | verbose_name_plural = _("workflow instance") 132 | 133 | 134 | class History(models.Model): 135 | """ 136 | workflow history 137 | """ 138 | PROCESS_TYPE = ( 139 | (0, _("SUBMIT")), 140 | (1, _("AGREE")), 141 | (3, _("DENY")), 142 | (4, _("TERMINATE")), 143 | ) 144 | index_weight = 5 145 | inst = models.ForeignKey(Instance,verbose_name=_("workflow instance")) 146 | node = models.ForeignKey(Node,verbose_name=_("workflow node"),blank=True,null=True) 147 | user = models.ForeignKey(User,verbose_name=_("submitter")) 148 | pro_time = models.DateTimeField(_("process time"),auto_now_add=True) 149 | pro_type = models.IntegerField(_("process type"),choices=PROCESS_TYPE,default=0) 150 | memo = models.CharField(_("memo"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True) 151 | 152 | def get_node_desc(self): 153 | if self.node: 154 | return self.node.name 155 | else: 156 | return u'启动' 157 | 158 | def get_action_desc(self): 159 | action_mapping = {0:u'提交',1:u'同意',3:u'拒绝',4:u'终止',} 160 | # print action_mapping 161 | if self.pro_type: 162 | return action_mapping[self.pro_type] 163 | else: 164 | return u'提交' 165 | 166 | def get_memo_desc(self): 167 | if self.memo: 168 | return self.memo 169 | else: 170 | return '' 171 | 172 | class Meta: 173 | verbose_name = _("workflow history") 174 | verbose_name_plural = _("workflow history") 175 | ordering = ['inst','pro_time'] 176 | 177 | 178 | class TodoList(models.Model): 179 | """ 180 | 181 | """ 182 | index_weight = 4 183 | code = models.CharField(_("code"),max_length=const.DB_CHAR_CODE_10,blank=True,null=True) 184 | inst = models.ForeignKey(Instance,verbose_name=_("workflow instance")) 185 | node = models.ForeignKey(Node,verbose_name=_("current node"),blank=True,null=True) 186 | app_name = models.CharField(_("app name"),max_length=const.DB_CHAR_NAME_60,blank=True,null=True) 187 | model_name = models.CharField(_("model name"),max_length=const.DB_CHAR_NAME_60,blank=True,null=True) 188 | user = models.ForeignKey(User,verbose_name=_("handler")) 189 | arrived_time = models.DateTimeField(_("arrived time"),auto_now_add=True) 190 | is_read = models.BooleanField(_("is read"),default=False) 191 | read_time = models.DateTimeField(_("read time"),blank=True,null=True) 192 | status = models.BooleanField(_("is done"),default=False) 193 | 194 | def save(self, force_insert=False, force_update=False, using=None, 195 | update_fields=None): 196 | super(TodoList,self).save(force_update,force_update,using,update_fields) 197 | if not self.code: 198 | self.code = 'TD%05d' % self.id 199 | self.save() 200 | 201 | def node_dsc(self): 202 | if self.node: 203 | return u'%s'%self.node.name 204 | else: 205 | return u'启动' 206 | 207 | def code_link(self): 208 | return format_html("{}", 209 | self.app_name,self.model_name,self.inst.object_id,self.code) 210 | code_link.allow_tags = True 211 | code_link.short_description = _("code") 212 | 213 | def href(self): 214 | import sys 215 | reload(sys) 216 | sys.setdefaultencoding("utf-8") 217 | ct = ContentType.objects.get(app_label=self.app_name,model=self.model_name) 218 | obj = ct.get_object_for_this_type(id=self.inst.object_id) 219 | title = u"%s" % (obj) 220 | return format_html("{}", 221 | self.app_name,self.model_name,self.inst.object_id,title) 222 | def modal_dsc(self): 223 | return u'%s'%(self.inst.modal.name) 224 | modal_dsc.short_description = u'业务流程' 225 | 226 | def start_time(self): 227 | return self.inst.start_time.strftime('%Y-%m-%d %H:%M') 228 | 229 | href.allow_tags = True 230 | href.short_description = _("description") 231 | node_dsc.short_description = _('current node') 232 | 233 | def submitter(self): 234 | if self.inst.starter.last_name or self.inst.starter.first_name: 235 | return u"%s%s"%(self.inst.starter.last_name,self.inst.starter.first_name) 236 | return u"%s"%(self.inst.starter.username) 237 | submitter.short_description = _("submitter") 238 | 239 | class Meta: 240 | verbose_name = _("workflow todo") 241 | verbose_name_plural = _("workflow todo") 242 | ordering = ['user','-arrived_time'] 243 | 244 | 245 | def get_modal(app_label,model_name): 246 | """ 247 | 248 | :param app_label: 249 | :param model_name: 250 | :return: 251 | """ 252 | try: 253 | return Modal.objects.get(app_name=app_label,model_name=model_name) 254 | except Exception,e: 255 | return None 256 | 257 | 258 | def get_instance(obj): 259 | """ 260 | 261 | :param obj: 262 | :return: 263 | """ 264 | if obj and obj._meta: 265 | modal = get_modal(obj._meta.app_label,obj._meta.model_name) 266 | if modal: 267 | try: 268 | return Instance.objects.get(modal=modal,object_id=obj.id) 269 | except Exception,e: 270 | return None 271 | else: 272 | return None 273 | -------------------------------------------------------------------------------- /workflow/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /文档/00产品资料-功能表.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/文档/00产品资料-功能表.doc -------------------------------------------------------------------------------- /文档/10使用手册-功能概述.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/文档/10使用手册-功能概述.doc -------------------------------------------------------------------------------- /文档/20使用手册-基本操作.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/文档/20使用手册-基本操作.doc -------------------------------------------------------------------------------- /文档/30使用手册-个人自助.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/文档/30使用手册-个人自助.doc -------------------------------------------------------------------------------- /文档/40使用手册-销售管理.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/文档/40使用手册-销售管理.doc -------------------------------------------------------------------------------- /文档/50使用手册-采购管理.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/文档/50使用手册-采购管理.doc -------------------------------------------------------------------------------- /文档/60使用手册-库存管理.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/文档/60使用手册-库存管理.doc -------------------------------------------------------------------------------- /文档/70使用手册-工作流管理.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyzsf/Django-ERP/de9d692a0292c244d08f85d192746b07a1d21765/文档/70使用手册-工作流管理.doc --------------------------------------------------------------------------------