chore: re-initialize git repository for deployment

This commit is contained in:
yi
2026-06-10 14:01:56 +08:00
commit 7783e965fd
131 changed files with 14355 additions and 0 deletions
+93
View File
@@ -0,0 +1,93 @@
# --- Python ---
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
*.egg
*.egg-info/
.eggs/
dist/
build/
pip-log.txt
pip-delete-this-directory.txt
# Virtual environments
.venv/
venv/
ENV/
env/
.python-version
# --- Django ---
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Collected static (if STATIC_ROOT differs from source)
staticfiles/
collected_static/
# Compiled translations (generate via compilemessages)
*.mo
# --- Environment & secrets ---
.env
.env.*
!.env.example
*.pem
*.key
# --- IDE / Editor ---
.idea/
.vscode/
*.sublime-project
*.sublime-workspace
*.swp
*.swo
*~
# --- OS ---
.DS_Store
.DS_Store?
Thumbs.db
ehthumbs.db
desktop.ini
# --- Cursor / local workflow ---
.cursor/
.work/
# --- Test & coverage ---
.coverage
.coverage.*
htmlcov/
.tox/
.nox/
.pytest_cache/
.mypy_cache/
.dmypy.json
dmypy.json
# --- Docker (local overrides & volumes) ---
docker-compose.override.yml
docker-compose.local.yml
*.volume/
# --- User uploads (MEDIA_ROOT) ---
# Keep template docs under upload/doc/
upload/data/*
!upload/data/.gitkeep
upload/inventory/*
!upload/inventory/.gitkeep
upload/project/*
!upload/project/.gitkeep
upload/offer sheet/*
!upload/offer sheet/.gitkeep
# --- Misc ---
*.bak
*.tmp
*.orig
.work/
+22
View File
@@ -0,0 +1,22 @@
FROM python:2.7-slim
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# Set work directory
WORKDIR /app
# Install Python dependencies
# Using PyMySQL which is pure python, so we might not need gcc/mysql-client-dev
COPY Install/requirements.txt /app/Install/requirements.txt
RUN pip install --no-cache-dir -r Install/requirements.txt
# Copy project
COPY . /app/
# Expose port
EXPOSE 8000
# Run server
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
+2413
View File
File diff suppressed because one or more lines are too long
+45
View File
@@ -0,0 +1,45 @@
-- 销售订单示例数据(依赖 basedata_partner / basedata_material 等基础数据)
-- 导入: mysql -uroot -proot mis < Install/SQL/sale_order_sample.sql
SET NAMES utf8mb4;
INSERT INTO `sale_saleorder`
(`id`,`begin`,`end`,`creator`,`modifier`,`creation`,`modification`,`code`,`order_date`,`deliver_date`,`title`,`description`,`contact`,`phone`,`fax`,`deliver_address`,`invoice_type`,`amount`,`discount_amount`,`status`,`org_id`,`partner_id`,`user_id`)
SELECT 1,'2025-05-20','9999-12-31','chengcai','chengcai',NOW(),NOW(),'SO00001','2025-05-10','2025-05-25','办公设备采购订单','含笔记本电脑及服务器,需上门安装调试。','张经理','13800138001',NULL,'上海市浦东新区张江高科技园区科苑路88号','10',24299.00,0.00,'9',1,17,11
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleorder WHERE code='SO00001');
INSERT INTO `sale_saleorder`
(`id`,`begin`,`end`,`creator`,`modifier`,`creation`,`modification`,`code`,`order_date`,`deliver_date`,`title`,`description`,`contact`,`phone`,`fax`,`deliver_address`,`invoice_type`,`amount`,`discount_amount`,`status`,`org_id`,`partner_id`,`user_id`)
SELECT 2,'2025-05-20','9999-12-31','chengcai','chengcai',NOW(),NOW(),'SO00002','2025-05-15','2025-06-01','配电设备批量采购','项目现场配电改造用材。','李工','13900139002',NULL,'江苏省南京市江宁区将军大道29号','10',6400.00,200.00,'1',1,17,11
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleorder WHERE code='SO00002');
INSERT INTO `sale_saleorder`
(`id`,`begin`,`end`,`creator`,`modifier`,`creation`,`modification`,`code`,`order_date`,`deliver_date`,`title`,`description`,`contact`,`phone`,`fax`,`deliver_address`,`invoice_type`,`amount`,`discount_amount`,`status`,`org_id`,`partner_id`,`user_id`)
SELECT 3,'2025-05-20','9999-12-31','chengcai','chengcai',NOW(),NOW(),'SO00003','2025-05-18','2025-05-22','水果批发季节订单','当季鲜果直采,分批配送。','王采购','13700137003',NULL,'浙江省杭州市余杭区良渚街道物流园A区','10',13962.00,0.00,'0',1,17,11
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleorder WHERE code='SO00003');
INSERT INTO `sale_saleorder`
(`id`,`begin`,`end`,`creator`,`modifier`,`creation`,`modification`,`code`,`order_date`,`deliver_date`,`title`,`description`,`contact`,`phone`,`fax`,`deliver_address`,`invoice_type`,`amount`,`discount_amount`,`status`,`org_id`,`partner_id`,`user_id`)
SELECT 4,'2025-05-20','9999-12-31','chengcai','chengcai',NOW(),NOW(),'SO00004','2025-05-20','2025-05-28','劳保用品集中采购','车间一线员工劳保手套补充采购。','赵主任','13600136004',NULL,'安徽省合肥市蜀山区望江西路666号','10',5500.00,0.00,'9',1,17,11
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleorder WHERE code='SO00004');
INSERT INTO `sale_saleitem` (`id`,`cnt`,`stock_price`,`sale_price`,`tax`,`create_time`,`status`,`event_time`,`master_id`,`material_id`,`measure_id`,`discount_price`)
SELECT 1,2.0000,NULL,7150.0000,'0.00',NOW(),0,NULL,1,53,1,NULL FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleitem WHERE id=1);
INSERT INTO `sale_saleitem` (`id`,`cnt`,`stock_price`,`sale_price`,`tax`,`create_time`,`status`,`event_time`,`master_id`,`material_id`,`measure_id`,`discount_price`)
SELECT 2,1.0000,NULL,9999.0000,'0.00',NOW(),0,NULL,1,54,1,NULL FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleitem WHERE id=2);
INSERT INTO `sale_saleitem` (`id`,`cnt`,`stock_price`,`sale_price`,`tax`,`create_time`,`status`,`event_time`,`master_id`,`material_id`,`measure_id`,`discount_price`)
SELECT 3,20.0000,NULL,320.0000,'0.00',NOW(),0,NULL,2,52,1,NULL FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleitem WHERE id=3);
INSERT INTO `sale_saleitem` (`id`,`cnt`,`stock_price`,`sale_price`,`tax`,`create_time`,`status`,`event_time`,`master_id`,`material_id`,`measure_id`,`discount_price`)
SELECT 4,500.0000,NULL,10.9200,'0.00',NOW(),0,NULL,3,40,3,NULL FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleitem WHERE id=4);
INSERT INTO `sale_saleitem` (`id`,`cnt`,`stock_price`,`sale_price`,`tax`,`create_time`,`status`,`event_time`,`master_id`,`material_id`,`measure_id`,`discount_price`)
SELECT 5,300.0000,NULL,15.9900,'0.00',NOW(),0,NULL,3,41,3,NULL FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleitem WHERE id=5);
INSERT INTO `sale_saleitem` (`id`,`cnt`,`stock_price`,`sale_price`,`tax`,`create_time`,`status`,`event_time`,`master_id`,`material_id`,`measure_id`,`discount_price`)
SELECT 6,200.0000,NULL,18.5250,'0.00',NOW(),0,NULL,3,42,3,NULL FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleitem WHERE id=6);
INSERT INTO `sale_saleitem` (`id`,`cnt`,`stock_price`,`sale_price`,`tax`,`create_time`,`status`,`event_time`,`master_id`,`material_id`,`measure_id`,`discount_price`)
SELECT 7,1000.0000,NULL,5.5000,'0.00',NOW(),0,NULL,4,5,1,NULL FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM sale_saleitem WHERE id=7);
+170
View File
@@ -0,0 +1,170 @@
# -*- coding: utf-8 -*-
"""导入销售订单示例数据。用法: python Install/load_sale_sample.py"""
import os
import sys
import datetime
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if BASE_DIR not in sys.path:
sys.path.insert(0, BASE_DIR)
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mis.settings')
import django
django.setup()
from decimal import Decimal
from django.contrib.auth.models import User
from django.db import transaction
from basedata.models import Partner, Material, Organization
from sale.models import SaleOrder, SaleItem
def recalc_order_amount(order):
total = Decimal('0')
for item in SaleItem.objects.filter(master=order):
price = item.sale_price or Decimal('0')
total += price * item.cnt
SaleOrder.objects.filter(pk=order.pk).update(amount=total)
order.amount = total
return total
SAMPLE_ORDERS = [
{
'code': 'SO00001',
'title': u'办公设备采购订单',
'order_date': datetime.date(2025, 5, 10),
'deliver_date': datetime.date(2025, 5, 25),
'contact': u'张经理',
'phone': '13800138001',
'deliver_address': u'上海市浦东新区张江高科技园区科苑路88号',
'description': u'含笔记本电脑及服务器,需上门安装调试。',
'status': '9',
'discount_amount': 0,
'items': [
{'material_code': 'IT5001', 'cnt': 2, 'sale_price': 7150.00},
{'material_code': 'IT5002', 'cnt': 1, 'sale_price': 9999.00},
],
},
{
'code': 'SO00002',
'title': u'配电设备批量采购',
'order_date': datetime.date(2025, 5, 15),
'deliver_date': datetime.date(2025, 6, 1),
'contact': u'李工',
'phone': '13900139002',
'deliver_address': u'江苏省南京市江宁区将军大道29号',
'description': u'项目现场配电改造用材。',
'status': '1',
'discount_amount': 200.00,
'items': [
{'material_code': 'IT9981', 'cnt': 20, 'sale_price': 320.00},
],
},
{
'code': 'SO00003',
'title': u'水果批发季节订单',
'order_date': datetime.date(2025, 5, 18),
'deliver_date': datetime.date(2025, 5, 22),
'contact': u'王采购',
'phone': '13700137003',
'deliver_address': u'浙江省杭州市余杭区良渚街道物流园A区',
'description': u'当季鲜果直采,分批配送。',
'status': '0',
'discount_amount': 0,
'items': [
{'material_code': 'IT9001', 'cnt': 500, 'sale_price': 11.00},
{'material_code': 'IT9002', 'cnt': 300, 'sale_price': 16.00},
{'material_code': 'IT9003', 'cnt': 200, 'sale_price': 19.00},
],
},
{
'code': 'SO00004',
'title': u'劳保用品集中采购',
'order_date': datetime.date(2025, 5, 20),
'deliver_date': datetime.date(2025, 5, 28),
'contact': u'赵主任',
'phone': '13600136004',
'deliver_address': u'安徽省合肥市蜀山区望江西路666号',
'description': u'车间一线员工劳保手套补充采购。',
'status': '9',
'discount_amount': 0,
'items': [
{'material_code': 'IT00005', 'cnt': 1000, 'sale_price': 5.50},
],
},
]
def load_samples():
partner = Partner.objects.filter(partner_type='C').first()
if not partner:
print(u'错误: 未找到客户类型合作伙伴,请先维护基础数据。')
return
user = User.objects.filter(username='chengcai').first() or User.objects.first()
org = Organization.objects.first()
creator = user.username if user else 'system'
today = datetime.date.today()
created = 0
skipped = 0
with transaction.atomic():
for data in SAMPLE_ORDERS:
if SaleOrder.objects.filter(code=data['code']).exists():
print(u'跳过已存在订单: %s' % data['code'])
skipped += 1
continue
order = SaleOrder(
code=data['code'],
title=data['title'],
order_date=data['order_date'],
deliver_date=data['deliver_date'],
partner=partner,
org=org,
user=user,
contact=data['contact'],
phone=data['phone'],
deliver_address=data['deliver_address'],
description=data['description'],
invoice_type='10',
status=data['status'],
discount_amount=data['discount_amount'],
amount=0,
begin=today,
end=datetime.date(9999, 12, 31),
creator=creator,
modifier=creator,
)
order.save()
for item_data in data['items']:
material = Material.objects.filter(code=item_data['material_code']).first()
if not material:
print(u'警告: 物料 %s 不存在,已跳过' % item_data['material_code'])
continue
item = SaleItem(
master=order,
material=material,
cnt=item_data['cnt'],
sale_price=item_data['sale_price'],
tax='0.00',
)
item.save()
if data['discount_amount'] > 0:
order.save()
total = recalc_order_amount(order)
print(u'已创建: %s %s 金额=%s 状态=%s' % (
order.code, order.title, total, order.get_status_display()
))
created += 1
print(u'\n完成: 新增 %d 条,跳过 %d 条。' % (created, skipped))
if __name__ == '__main__':
load_samples()
+4
View File
@@ -0,0 +1,4 @@
Django==1.10.6
PyMySQL==0.7.9
xlwt==1.0.0
xlrd==1.2.0
+127
View File
@@ -0,0 +1,127 @@
# 智捷ERP (SmartJet ERP) 操作手册
智捷ERP 是一款基于 Django 1.10.6 深度定制的企业资源计划 (ERP) 管理软件。系统经过现代 UI 重构与 Docker 化部署优化,提供销售管理、采购管理、库存管理、组织架构、人力资源及自动化工作流审批等核心功能。
![image-20260610135601841](images/image-20260610135601841.png)
---
## 1. 快速开始
### 1.1 环境要求
- Docker
- Docker Compose
### 1.2 一键部署
在项目根目录下执行以下命令,即可启动 Web 服务与 MySQL 数据库:
```bash
docker compose -f docker-compose.full.yml up -d --build
```
### 1.3 访问地址
- **后台管理**: [http://localhost:6010/admin/](http://localhost:6010/admin/)
- **数据库服务**: `localhost:6011` (容器内 3306)
### 1.4 默认账号
系统预置了管理员账号用于演示:
| 角色 | 用户名 | 密码 | 说明 |
|------|--------|------|------|
| 超级管理员 | xinmi_admin | admin123456 | 全新创建的超级用户,建议首选使用 |
---
## 2. 核心功能模块
智捷ERP 采用现代化的“侧边栏 + 高密度工作台”布局,确保业务数据直观可见。
![image-20260610135614258](images/image-20260610135614258.png)
### 2.1 仪表盘 (Dashboard)
**路径**: `/admin/`
**功能概述**: 实时展现企业核心经营指标。
- **数据看板**: 销售总额、待办审批、库存预警、系统在线率。
- **趋势分析**: 关键指标下方的 Sparkline 动态折线图。
- **快捷操作**: 一键新建销售单、采购入库、员工入职。
### 2.2 核心业务
- **销售管理**: `/admin/sale/saleorder/` —— 涵盖报价单、销售订单、收款计划跟踪。
- **采购管理**: `/admin/purchase/purchaseorder/` —— 支持采购申请、订单审批、入库确认。
- **库存管理**: `/admin/invent/inventory/` —— 实时盘点、低水位报警、批次溯源。
![image-20260610135625703](images/image-20260610135625703.png)
![image-20260610135636514](images/image-20260610135636514.png)
![image-20260610135644978](images/image-20260610135644978.png)
![image-20260610135653450](images/image-20260610135653450.png)
![image-20260610135704180](images/image-20260610135704180.png)
![image-20260610135711248](images/image-20260610135711248.png)
### 2.3 组织与人力
- **组织架构**: `/admin/organ/organization/` —— 维护企业多级部门与岗位树。
- **员工档案**: `/admin/basedata/employee/` —— 员工全生命周期管理、五险一金基础配置。
![image-20260610135719363](images/image-20260610135719363.png)
![image-20260610135727597](images/image-20260610135727597.png)
![image-20260610135737510](images/image-20260610135737510.png)
### 2.4 流程协同
- **审批中心**: `/admin/workflow/instance/` —— 流程实例监控。
- **待办任务**: `/admin/workflow/todolist/` —— 个人审批任务列表,支持紧急程度分级。
![image-20260610135747819](images/image-20260610135747819.png)
![image-20260610135757306](images/image-20260610135757306.png)
---
## 3. 视觉与体验优化 (Next-Gen UI 4.0)
本项目已完成 1:1 高保真 UI 复刻,具有以下设计特征:
- **高级配色**: 采用 Indigo & Slate 商务配色。
- **极致紧凑**: Ultra-Compact 模式,单屏展示数据量提升 40% 以上。
- **响应式布局**: 侧边栏固定,超宽表格自动开启内部横向滚动保护。
---
## 4. 技术维护
### 4.1 数据库初始化
数据库初始化 SQL 位于 `Install/SQL/mis.sql`,Docker 启动时会自动挂载并执行。
### 4.2 静态资源更新
若修改了 CSS 或 JS,请在容器内执行:
```bash
docker compose exec web python manage.py collectstatic --noinput
```
---
## 5. 常见问题 (FAQ)
**Q: 登录时提示“请输入正确的用户名和密码”?**
A: 可能是由于默认加密算法版本差异,请通过控制台重置:
`docker compose exec web python manage.py changepassword admin`
**Q: 列表页面字段太多,显示超宽?**
A: 系统已开启横向滚动条保护,您可以在表格区域内左右滑动查看完整数据。
---
© 2026 智捷ERP (SmartJet ERP) · 让企业管理更智能
+2
View File
@@ -0,0 +1,2 @@
default_app_config = 'basedata.apps.BaseDataConfig'
+339
View File
@@ -0,0 +1,339 @@
# coding=utf-8
from django.contrib import admin
from django.forms import models
from django.forms import fields,TextInput,Textarea
from django.contrib import messages
from django.utils.translation import ugettext_lazy as _
from django.contrib.contenttypes.admin import GenericTabularInline
from common import generic
from basedata.models import ValueList,ValueListItem,Address,Partner,BankAccount,Project,Measure,Material,Brand,\
Category,Warehouse,TechnicalParameterName,TechnicalParameterValue,Trade,ExpenseAccount,Employee,Family,Education,\
WorkExperience,ExtraParam,DataImport,Document
class ValueListItemInline(admin.TabularInline):
model = ValueListItem
exclude = ['group_code']
def get_extra(self, request, obj=None, **kwargs):
if obj:
return 0
else:
return 3
class ValueListAdmin(generic.BOAdmin):
CODE_NUMBER_WIDTH = 3
CODE_PREFIX = 'S'
list_display = ['code', 'name', 'module', 'status']
fields = (('code',),('name',),('module',),('status','init','locked',),('locked_by','lock_time',))
raw_id_fields = ['module']
readonly_fields = ['locked_by','lock_time']
inlines = [ValueListItemInline]
search_fields = ['code','name']
def save_model(self, request, obj, form, change):
super(ValueListAdmin,self).save_model(request,obj,form,change)
obj.valuelistitem_set.update(group_code=obj.code)
class AddressAdmin(generic.BOAdmin):
list_display = ['address','phone','contacts']
exclude = ['content_type','object_id','creator','modifier','creation','modification','begin','end']
class AddressInline(GenericTabularInline):
model = Address
exclude = ['content_type','object_id','creator','modifier','creation','modification','begin','end']
extra = 1
class BankAccountInline(admin.TabularInline):
model = BankAccount
fields = ['account','title','memo']
def get_extra(self, request, obj=None, **kwargs):
if obj:
return 0
else:
return 1
class PartnerForm(models.ModelForm):
tax_address = fields.CharField(widget=TextInput(attrs={'size': 119,}),required=False,label=_("tax address"))
memo = fields.CharField(widget=Textarea(attrs={'rows':3,'cols':85}),required=False,label=_("memo"))
class Meta:
model = Partner
fields = '__all__'
class PartnerAdmin(generic.BOAdmin):
list_display = ['code','name','partner_type','level']
list_display_links = ['code','name']
fields = (('code','name',),('short','pinyin',),('partner_type','level'),('tax_num','tax_account',),
('tax_address',),('contacts','phone',),('memo',),)
search_fields = ['code','name','pinyin']
form = PartnerForm
save_on_top = True
inlines = [AddressInline,BankAccountInline]
def get_queryset(self, request):
if request.user.is_superuser or (request.user.has_perm('basedate.view_all_customer') and request.user.has_perm('basedate.view_all_supplier')):
return super(PartnerAdmin,self).get_queryset(request)
elif request.user.has_perm('basedata.view_all_customer'):
return super(PartnerAdmin,self).get_queryset(request).filter(partner_type='C')
else:
return super(PartnerAdmin,self).get_queryset(request).filter(partner_type='S')
class ProjectForm(models.ModelForm):
income = fields.DecimalField(required=False,widget=TextInput(attrs={'readonly':'true'}))
expand = fields.DecimalField(required=False,widget=TextInput(attrs={'readonly':'true'}))
class Meta:
model = Project
fields = '__all__'
class ProjectAdmin(generic.BOAdmin):
CODE_PREFIX = 'PJ'
list_display = ['code','name','status','income','expand']
list_display_links = ['code','name']
fields = (
('code','name',),('short','pinyin',),
('partner',),('status','prj_type',),
('description',),
('budget','income','expand',),('blueprint',),('offer',),('business',),('users',),
)
search_fields = ['code','name']
readonly_fields = ['status']
raw_id_fields = ['partner']
filter_horizontal = ['users']
form = ProjectForm
class WarehouseAdmin(admin.ModelAdmin):
list_display = ['code','name','location']
filter_horizontal = ['users']
def save_model(self, request, obj, form, change):
super(WarehouseAdmin,self).save_model(request,obj,form,change)
try:
code = getattr(obj,'code')
if not code:
obj.code = '%s%02d' % ('A',obj.id)
obj.save()
except Exception,e:
self.message_user(request,'ERROR:%s' % e,level=messages.ERROR)
class BrandAdmin(admin.ModelAdmin):
list_display = ['name','pinyin']
class MeasureAdmin(admin.ModelAdmin):
list_display = ['code','name','status']
class CategoryAdmin(admin.ModelAdmin):
list_display = ['code','name','path']
def save_model(self, request, obj, form, change):
super(CategoryAdmin,self).save_model(request,obj,form,change)
try:
code = getattr(obj,'code')
if not code:
obj.code = '%s%02d' % ('F',obj.id)
obj.save()
if obj.parent:
if obj.parent.path:
obj.path = obj.parent.path + '/'+obj.parent.name
else:
obj.path = obj.parent.name
obj.save()
except Exception,e:
self.message_user(request,'ERROR:%s' % e,level=messages.ERROR)
class MaterialForm(models.ModelForm):
name = fields.CharField(widget=TextInput(attrs={"size":"119"}),label=_("material name"))
spec = fields.CharField(widget=TextInput(attrs={"size":"119"}),required=False,label=_("specifications"))
class Mata:
model = Material
fields = '__all__'
class ExtraParamInline(admin.TabularInline):
model = ExtraParam
fields = ['name','data_type','data_source']
def get_extra(self, request, obj=None, **kwargs):
if obj:
return 0
else:
return 1
class MaterialAdmin(generic.BOAdmin):
CODE_PREFIX = 'IT'
CODE_NUMBER_WIDTH = 5
list_display = ['code','name','spec','tp']
list_display_links = ['code','name']
list_filter = ['brand','tp']
search_fields = ['code','name']
fields = (
('code','barcode'),('name',),('spec',),
('brand',),('category',),('status','is_equip','can_sale','is_virtual',),
('warehouse',),('tp',),('measure',),('stock_price','purchase_price','sale_price',),
)
filter_horizontal = ['measure']
inlines = [ExtraParamInline]
form = MaterialForm
class TechParamValueInline(admin.TabularInline):
model = TechnicalParameterValue
class TechParamNameAdmin(admin.ModelAdmin):
list_display = ['name','category']
inlines = [TechParamValueInline]
class TradeAdmin(admin.ModelAdmin):
list_display = ['code','name','parent']
class ExpenseAdmin(generic.BOAdmin):
CODE_PREFIX = 'FC'
list_display = ['code','name','category']
list_display_links = ['code','name']
list_filter = ['category']
search_fields = ['name']
class FamilyForm(models.ModelForm):
name = fields.CharField(widget=TextInput(attrs={"size":"25"}),label=_("name"))
phone = fields.CharField(widget=TextInput(attrs={"size":"25"}),label=_("phone"))
class Meta:
model = Family
fields = '__all__'
class FamilyInline(admin.TabularInline):
model = Family
exclude = ['creator','modifier','creation','modification','begin','end']
form = FamilyForm
extra = 1
class EducationInline(admin.TabularInline):
model = Education
exclude = ['creator','modifier','creation','modification']
extra = 0
class WorkExperienceInline(admin.TabularInline):
model = WorkExperience
exclude = ['creator','modifier','creation','modification']
extra = 1
class EmployeeAdmin(generic.BOAdmin):
CODE_PREFIX = '1'
list_display = ['code','name','position','gender','idcard','age','work_age','literacy','phone','email']
search_fields = ['code','name','idcard','pinyin']
fieldsets = [
(None,{'fields':[('code','phone',),('name','pinyin',),('gender','birthday',),('idcard','country',),
('position',),('rank','category'),('status','ygxs',),('workday','startday',)]}),
(_('other info'),{'fields':[('hometown','address',),('banknum','bankname',),('email','office',),
('emergency','literacy',),('religion','marital',),('party','nation',),('spjob','health',),
('major','degree',),('tag1','tag2',),('tag3','tag4',),('user',),],'classes':['collapse']}),
]
readonly_fields = ['status','ygxs','rank','category']
inlines = [FamilyInline,EducationInline,WorkExperienceInline]
raw_id_fields = ['user']
def get_queryset(self, request):
if request.user.is_superuser or request.user.has_perm('basedata.view_all_employee'):
return super(EmployeeAdmin,self).get_queryset(request)
else:
return super(EmployeeAdmin,self).get_queryset(request).filter(user=request.user)
def get_readonly_fields(self, request, obj=None):
if request.user.is_superuser:
return []
else:
return ['status','ygxs','rank','category','position','user']
class DataImportAdmin(generic.BOAdmin):
list_display = ['imp_date','title','status']
list_display_links = ['imp_date','title']
raw_id_fields = ['content_type']
readonly_fields = ['status']
extra_buttons = [{'href':'action','title':_('import')}]
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
if object_id:
obj = DataImport.objects.get(id=object_id)
if obj.status == '1':
extra_context = extra_context or {}
extra_context.update(dict(readonly=True))
return super(DataImportAdmin,self).changeform_view(request,object_id,form_url,extra_context)
class DocumentForm(models.ModelForm):
title = fields.CharField(widget=TextInput(attrs={"size":"119"}),label=_("title"))
keywords = fields.CharField(widget=TextInput(attrs={"size":"119"}),label=_("keywords"))
class Meta:
model = Document
fields = '__all__'
class DocumentAdmin(generic.BOAdmin):
CODE_PREFIX = 'FD'
CODE_NUMBER_WIDTH = 4
list_display = ['code','title','keywords','tp','business_domain','status','creation']
list_display_links = ['code','title']
fields = (('code','status',),('title',),('keywords',),('description',),('business_domain','tp',),('attach',))
readonly_fields = ['status']
list_filter = ['tp','business_domain']
search_fields = ['title','keywords','code']
form = DocumentForm
actions = ['publish']
date_hierarchy = 'begin'
def get_readonly_fields(self, request, obj=None):
if obj and obj.status=='1':
return ['code','status','title','keywords','description','business_domain','tp','attach',]
else:
return ['status']
def publish(self,request,queryset):
import datetime
cnt = queryset.filter(status='0').update(status='1',pub_date=datetime.datetime.now())
self.message_user(request,u'%s 个文档发布成功'%cnt)
publish.short_description = _('publish selected %(verbose_name_plural)s')
# admin.site.register(Address,AddressAdmin)
admin.site.register(ValueList,ValueListAdmin)
admin.site.register(Partner,PartnerAdmin)
admin.site.register(Project,ProjectAdmin)
admin.site.register(Material,MaterialAdmin)
admin.site.register(Warehouse,WarehouseAdmin)
admin.site.register(Brand,BrandAdmin)
admin.site.register(Measure,MeasureAdmin)
admin.site.register(Category,CategoryAdmin)
admin.site.register(TechnicalParameterName,TechParamNameAdmin)
admin.site.register(Trade,TradeAdmin)
admin.site.register(ExpenseAccount,ExpenseAdmin)
admin.site.register(Employee,EmployeeAdmin)
admin.site.register(DataImport,DataImportAdmin)
admin.site.register(Document,DocumentAdmin)
+9
View File
@@ -0,0 +1,9 @@
__author__ = 'zhugl'
# created at 15-4-22
from django.apps.config import AppConfig
from django.utils.translation import ugettext_lazy as _
class BaseDataConfig(AppConfig):
name = 'basedata'
verbose_name = _('BaseData')
+657
View File
@@ -0,0 +1,657 @@
# coding=utf-8
from django.db import models
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
from django.utils.translation import ugettext_lazy as _
from django.utils.text import force_text
from common import const
from common import generic
from syscfg.models import Module,Site
from organ.models import Organization,Position
import datetime
from plugin.xls import ExcelManager
class ValueList(generic.BO):
"""
值列表
"""
index_weight = 9
code = models.CharField(_("list code"),max_length=const.DB_CHAR_CODE_6,blank=True,null=True)
name = models.CharField(_("list name"),max_length=const.DB_CHAR_NAME_40)
module = models.ForeignKey(Module,verbose_name=_("module"),blank=True,null=True)
status = models.BooleanField(_("in use"),default=True)
init = models.BooleanField(_("is init"),default=False)
locked = models.BooleanField(_("is locked"),default=False)
locked_by = models.ForeignKey(User,verbose_name=_("locked by"),blank=True,null=True)
lock_time = models.DateTimeField(_("locked time"),null=True,blank=True)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
super(ValueList,self).save(force_insert,force_update,using,update_fields)
sql = 'update basedata_valuelistitem set group_code = %s where groud_id=%s'
params = [self.code,self.id]
generic.update(sql,params)
class Meta:
verbose_name = _('value list')
verbose_name_plural = _('value list')
class ValueListItem(models.Model):
"""
值列表项
"""
group = models.ForeignKey(ValueList,verbose_name=_("list group"))
group_code = models.CharField(max_length=const.DB_CHAR_CODE_6,blank=True,null=True)
code = models.CharField(_("item code"),max_length=const.DB_CHAR_CODE_6,blank=True,null=True)
name = models.CharField(_("item name"),max_length=const.DB_CHAR_NAME_40)
status = models.BooleanField(_("in use"),default=True)
weight = models.IntegerField(_("weight"),null=True,default=9)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
if not self.code:
cnt = self.group.valuelistitem_set.count()+1
self.code = "%02d" % cnt
self.group_code = self.group.code
super(ValueListItem,self).save(force_insert,force_update,using,update_fields)
def __unicode__(self):
return "%s-%s" % (self.code,self.name)
class Meta:
verbose_name = _('list item')
verbose_name_plural = _('list item')
ordering = ['weight','code']
index_together = ['group','group_code']
def get_value_list(group):
"""
:param group:
:return:
"""
if group:
return tuple([(item.code, item.name) for item in ValueListItem.objects.filter(group_code__exact=group,status=1)])
else:
return None
class Address(generic.BO):
"""
地址
"""
ADDRESS_TYPE = get_value_list('S011')
address_type = models.CharField(_("address type"),max_length=const.DB_CHAR_CODE_2,choices=ADDRESS_TYPE,default='01')
address = models.CharField(_("address"),max_length=const.DB_CHAR_NAME_120)
zipcode = models.CharField(_("zipcode"),max_length=const.DB_CHAR_CODE_8,blank=True,null=True)
phone = models.CharField(_("phone"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
contacts = models.CharField(_("contacts"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
content_type = models.ForeignKey(ContentType,blank=True,null=True)
object_id = models.PositiveIntegerField(blank=True,null=True)
content_object = GenericForeignKey('content_type', 'object_id')
class Meta:
verbose_name = _('address')
verbose_name_plural = _('address')
class Partner(generic.BO):
"""
合作伙伴
"""
index_weight = 3
PARTNER_TYPE = (
('C', _('Customer')),
('S', _('Supplier')),
)
LEVEL = (
('A','A'),
('B','B'),
('C','C'),
('D','D'),
)
org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True)
code = models.CharField(_("partner code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
name = models.CharField(_("partner name"),max_length=const.DB_CHAR_NAME_120)
short = models.CharField(_("short name"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
pinyin = models.CharField(_("pinyin"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
partner_type = models.CharField(_("type"),max_length=const.DB_CHAR_CODE_2,choices=PARTNER_TYPE,default='C')
level = models.CharField(_("level"),max_length=const.DB_CHAR_CODE_2,choices=LEVEL,default='C')
tax_num = models.CharField(_("tax num"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
tax_address = models.CharField(_("tax address"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
tax_account = models.CharField(_("tax account"),max_length=const.DB_CHAR_NAME_80,blank=True,null=True)
contacts = models.CharField(_("contacts"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
phone = models.CharField(_("phone"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
memo = models.TextField(_("memo"),blank=True,null=True)
class Meta:
verbose_name = _('partner')
verbose_name_plural = _('partner')
permissions = (
('view_all_customer',_("view all customer")),
('view_all_supplier',_("view all supplier")),
)
class BankAccount(generic.BO):
"""
银行账户 organization
"""
account = models.CharField(_("account num"),max_length=const.DB_CHAR_NAME_40)
title = models.CharField(_("bank name"),max_length=const.DB_CHAR_NAME_40)
memo = models.CharField(_("memo"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
partner = models.ForeignKey(Partner,verbose_name=_("partner"),blank=True,null=True)
org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True)
def __unicode__(self):
name = ''
if self.org:
name = self.org.name
elif self.partner:
name = self.partner.name
return u"%s %s %s" % (name,self.account,self.title)
class Meta:
verbose_name = _('bank account')
verbose_name_plural = _('bank account')
class Project(generic.BO):
"""
工程项目
"""
STATUS = get_value_list('S012')
TYPES = get_value_list('S013')
index_weight = 1
code = models.CharField(_("project code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
name = models.CharField(_("project name"),max_length=const.DB_CHAR_NAME_120)
short = models.CharField(_("short name"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
pinyin = models.CharField(_("pinyin"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
partner = models.ForeignKey(Partner,blank=True,null=True,verbose_name=_("partner"),limit_choices_to={"partner_type":"C"})
status = models.CharField(_("status"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,default='00',choices=STATUS)
prj_type = models.CharField(_("project type"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=TYPES,default='00')
description = models.TextField(_("description"),blank=True,null=True)
budget = models.DecimalField(_("budget"),max_digits=10,decimal_places=2,blank=True,null=True)
income = models.DecimalField(_("income"),max_digits=10,decimal_places=2,blank=True,null=True)
expand = models.DecimalField(_("expand"),max_digits=10,decimal_places=2,blank=True,null=True)
blueprint = models.FileField(_("blueprint"),upload_to='project',blank=True,null=True)
offer = models.FileField(_("offer sheet"),upload_to='offer sheet',blank=True,null=True)
business = models.FileField(_("business document"),upload_to='project',blank=True,null=True)
users = models.ManyToManyField(User,verbose_name=_("related users"),blank=True)
org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True)
class Meta:
verbose_name = _('project')
verbose_name_plural = _('project')
class Warehouse(models.Model):
"""
仓库
"""
index_weight = 6
code = models.CharField(_("code"),max_length=const.DB_CHAR_CODE_6,blank=True,null=True)
name = models.CharField(_("name"),max_length=const.DB_CHAR_NAME_40)
status = models.BooleanField(_("in use"),default=True)
location = models.CharField(_("location"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
users = models.ManyToManyField(User,verbose_name=_("related users"),blank=True)
org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True)
def __unicode__(self):
return '%s' % self.name
class Meta:
verbose_name = _('warehouse')
verbose_name_plural = _('warehouse')
class Measure(models.Model):
"""
计量单位
"""
index_weight = 5
code = models.CharField(_("code"),max_length=const.DB_CHAR_CODE_6,blank=True,null=True)
name = models.CharField(_("name"),max_length=const.DB_CHAR_NAME_20)
status = models.BooleanField(_("in use"),default=True)
def __unicode__(self):
return '%s' % self.name
class Meta:
verbose_name = _('measure')
verbose_name_plural = _('measure')
class Trade(models.Model):
"""
国民经济行业分类
"""
index_weight = 102
code = models.CharField(_("code"),max_length=const.DB_CHAR_CODE_6)
name = models.CharField(_("name"),max_length=const.DB_CHAR_NAME_120)
memo = models.CharField(_("memo"),max_length=const.DB_CHAR_NAME_120,null=True,blank=True)
parent = models.ForeignKey('self',verbose_name=_("parent"),null=True,blank=True)
def __unicode__(self):
return '%s' % self.name
class Meta:
verbose_name = _('trade')
verbose_name_plural = _('trade')
ordering = ['code']
class Brand(models.Model):
"""
品牌
"""
index_weight = 101
trade = models.ForeignKey(Trade,verbose_name=_("trade"),null=True,blank=True)
name = models.CharField(_("name"),max_length=const.DB_CHAR_NAME_120)
pinyin = models.CharField(_("pinyin"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
weight = models.IntegerField(_("weight"),blank=True,null=True,default=99)
def __unicode__(self):
return '%s' % self.name
class Meta:
verbose_name = _('brand')
verbose_name_plural = _('brand')
class Category(models.Model):
"""
分类
"""
index_weight = 100
trade = models.ForeignKey(Trade,verbose_name=_("trade"),null=True,blank=True)
parent = models.ForeignKey('self',verbose_name=_("parent"),null=True,blank=True)
code = models.CharField(_("code"),max_length=const.DB_CHAR_CODE_6,null=True,blank=True)
name = models.CharField(_("name"),max_length=const.DB_CHAR_NAME_120)
path = models.CharField(_("path"),max_length=const.DB_CHAR_NAME_200,null=True,blank=True)
def __unicode__(self):
return '%s' % self.name
class Meta:
verbose_name = _('category')
verbose_name_plural = _('category')
class TechnicalParameterName(models.Model):
"""
技术参数-名称,将技术参数绑定于物料分类上,在此分类下的物料自动继承全部技术参数
"""
index_weight = 7
category = models.ForeignKey(Category,verbose_name=_("material category"))
name = models.CharField(_("name"),max_length=const.DB_CHAR_NAME_40)
status = models.BooleanField(_("in use"),default=True)
def __unicode__(self):
return '%s' % self.name
class Meta:
verbose_name = _('technical parameter')
verbose_name_plural = _('technical parameter')
class TechnicalParameterValue(models.Model):
"""
技术参数-值,将技术参数绑定于物料分类上,在此分类下的物料自动继承全部技术参数
"""
tech_name = models.ForeignKey(TechnicalParameterName,verbose_name=_("technical name"))
value = models.CharField(_("value"),max_length=const.DB_CHAR_NAME_80)
description = models.CharField(_("description"),max_length=const.DB_CHAR_NAME_80,null=True,blank=True)
def __unicode__(self):
return '%s' % self.value
class Meta:
verbose_name = _('technical value')
verbose_name_plural = _('technical value')
class Material(generic.BO):
"""
物料
"""
index_weight = 4
code = models.CharField(_("material code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
barcode = models.CharField(_("bar code"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
name = models.CharField(_("material name"),max_length=const.DB_CHAR_NAME_120)
spec = models.CharField(_("specifications"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
pinyin = models.CharField(_("pinyin"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
brand = models.ForeignKey(Brand,blank=True,null=True,verbose_name=_("brand"))
category = models.ForeignKey(Category,blank=True,null=True,verbose_name=_("category"))
tp = models.CharField(_('mt type'),blank=True,null=True,max_length=const.DB_CHAR_CODE_2,choices=const.get_value_list('S054'),default='10')
status = models.BooleanField(_("in use"),default=True)
is_equip = models.BooleanField(_("is equipment"),default=False)
can_sale = models.BooleanField(_("can sale"),default=True)
is_virtual = models.BooleanField(_("is virtual"),default=False)
warehouse = models.ForeignKey(Warehouse,blank=True,null=True,verbose_name=_("warehouse"))
measure = models.ManyToManyField(Measure,verbose_name=_("measure"))
params = models.ManyToManyField(TechnicalParameterValue,verbose_name=_("technical parameter"),through='MaterialParam')
stock_price = models.DecimalField(_("stock price"),max_digits=14,decimal_places=4,blank=True,null=True)
purchase_price = models.DecimalField(_("purchase price"),max_digits=14,decimal_places=4,blank=True,null=True)
sale_price = models.DecimalField(_("sale price"),max_digits=14,decimal_places=4,blank=True,null=True)
org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True)
def __unicode__(self):
return "%s %s" % (self.code,self.name)
class Meta:
verbose_name = _('material')
verbose_name_plural = _('material')
ordering = ['tp','code']
class MaterialParam(models.Model):
"""
"""
material = models.ForeignKey(Material)
param_value = models.ForeignKey(TechnicalParameterValue)
param_name = models.ForeignKey(TechnicalParameterName,blank=Trade,null=True)
creation = models.DateField(auto_now_add=True)
def __unicode__(self):
return '%s' % self.param_value
class Meta:
verbose_name = _('material parameter')
verbose_name_plural = _('material parameter')
class ExtraParam(models.Model):
"""
"""
DATA_TYPE = (
('CHAR',_('CHAR')),
('NUM',_('NUMBER')),
('DATE',_('DATE')),
)
material = models.ForeignKey(Material,verbose_name=_("material"))
name = models.CharField(_("name"),max_length=const.DB_CHAR_NAME_40)
data_type = models.CharField(_("data type"),default='CHAR',choices=DATA_TYPE,max_length=const.DB_CHAR_CODE_6)
data_source = models.CharField(_("data source"),blank=True,null=True,max_length=const.DB_CHAR_NAME_40)
def __unicode__(self):
return "%s" % self.name
class Meta:
verbose_name = _("extra param")
verbose_name_plural = _("extra params")
class ExpenseAccount(generic.BO):
"""
费用科目
"""
CATEGORY = (
('HR',_('HR-DOMAIN')),
('OF',_('OFFICE-DOMAIN')),
('PU',_('PUBLIS-DOMAIN')),
('MU',_('MUNADOMAIN')),
('BU',_('BUSINESS')),
('OT',_('OTHER')),
)
index_weight = 10
code = models.CharField(_("code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
name = models.CharField(_("name"),max_length=const.DB_CHAR_NAME_120)
category = models.CharField(_("category"),max_length=const.DB_CHAR_CODE_4,choices=CATEGORY,default='PU')
description = models.TextField(_("description"),blank=True,null=True)
parent = models.ForeignKey('self',verbose_name=_("parent"),null=True,blank=True)
status = models.BooleanField(_("in use"),default=True)
org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True)
class Meta:
verbose_name = _('expenses account')
verbose_name_plural = _('expenses account')
ordering = ['category','code']
class Employee(generic.BO):
"""
职员信息
"""
index_weight = 2
code = models.CharField(_("employee number"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
phone = models.CharField(_("phone"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
organization = models.ForeignKey(Organization,verbose_name = _('organization'),null=True,blank=True)
name = models.CharField(_("employee name"),max_length=const.DB_CHAR_NAME_120)
pinyin = models.CharField(_("pinyin"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
birthday = models.DateField(_("birthday"),blank=True,null=True)
gender = models.CharField(_("gender"),max_length=const.DB_CHAR_CODE_2,choices=const.get_value_list('gender'),default='1')
idcard = models.CharField(_("id card"),max_length=const.DB_CHAR_NAME_20)
country = models.CharField(_("nationality"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,default='CN',choices=const.get_value_list('S022'))
hometown = models.CharField(_("hometown"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
address = models.CharField(_("home address"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
banknum = models.CharField(_("bank account"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
bankname = models.CharField(_("bank name"),max_length=const.DB_CHAR_NAME_80,blank=True,null=True)
emergency = models.CharField(_("emergency contacts"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
email = models.CharField(_("email"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
office = models.CharField(_("office phone"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
position = models.ForeignKey(Position,verbose_name = _('position'))
rank = models.CharField(_("employee rank"),max_length=const.DB_CHAR_CODE_2,default='00',choices=const.get_value_list('S017'))
workday = models.DateField(_("workday"),blank=True,null=True)
startday = models.DateField(_("start date"),blank=True,null=True)
religion = models.CharField(_("religion"),max_length=const.DB_CHAR_CODE_2,default='00',choices=const.get_value_list('S020'),blank=True,null=True,)
marital = models.CharField(_("marital status"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S023'),default='10')
party = models.CharField(_("political party"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S026'),default='13')
nation = models.CharField(_("nation"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S021'),default='01')
ygxs = models.CharField(_("employ ygxs"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S019'),default='2')
status = models.CharField(_("employ status"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S016'),default='10')
category = models.CharField(_("employ category"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S018'),default='21')
literacy = models.CharField(_("literacy"),max_length=const.DB_CHAR_CODE_2,default='10',choices=const.get_value_list('S024'),blank=True,null=True)
major = models.CharField(_("major type"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S038'),default='99')
degree = models.CharField(_("major degree"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S037'),default='4')
spjob = models.CharField(_("special job"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S042'),default='00')
health = models.CharField(_("health"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S043'),default='1')
tag1 = models.CharField(_("tag1 fzjr"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S039'),default='99')
tag2 = models.CharField(_("tag2 dwld"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S040'),default='9')
tag3 = models.CharField(_("tag3 dsjs"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S041'),default='00')
tag4 = models.CharField(_("tag4 byzk"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S027'),default='0')
user = models.ForeignKey(User,verbose_name=_("user"),blank=True,null=True)
def age(self):
import datetime
if self.birthday:
cnt = datetime.date.today().year-self.birthday.year
return cnt
def work_age(self):
import datetime
if self.birthday and self.workday:
cnt = datetime.date.today().year-self.workday.year
return cnt
def __unicode__(self):
return u'%s %s'%(self.code,self.name)
age.short_description = u'年龄'
work_age.short_description = u'工龄'
class Meta:
verbose_name = _("employee")
verbose_name_plural = _("employee")
permissions = (
('view_all_employee',_("view all employee")),
)
class Family(generic.BO):
"""
家庭成员
"""
relation = models.CharField(_("family title"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S025'))
status = models.CharField(_("social status"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S029'),default='17')
name = models.CharField(_("name"),max_length=const.DB_CHAR_NAME_60)
birthday = models.DateField(_("birthday"),blank=True,null=True)
organization = models.CharField(_("organization"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
phone = models.CharField(_("phone"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
emergency = models.BooleanField(_("emergency"),default=False)
employee = models.ForeignKey(Employee,verbose_name=_("employee"))
class Meta:
verbose_name = _("family member")
verbose_name_plural = _("family member")
class Education(generic.BO):
"""
教育履历
"""
edu_type = models.CharField(_("edu type"),max_length=const.DB_CHAR_CODE_2,choices=const.get_value_list('S035'),default='1')
school = models.CharField(_("school"),max_length=const.DB_CHAR_NAME_120)
major = models.CharField(_("major"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
degree = models.CharField(_("major degree"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S037'),default='4')
employee = models.ForeignKey(Employee,verbose_name=_("employee"))
class Meta:
verbose_name = _("education experience")
verbose_name_plural = _("education experience")
class WorkExperience(generic.BO):
"""
工作履历
"""
organization = models.CharField(_("organization"),max_length=const.DB_CHAR_NAME_120)
position = models.CharField(_("position"),max_length=const.DB_CHAR_NAME_120)
employee = models.ForeignKey(Employee,verbose_name=_("employee"))
class Meta:
verbose_name = _("work experience")
verbose_name_plural = _("work experience")
class DataImport(generic.BO):
"""
Data import
"""
actions = {}
STATUS = (
('0',_('NEW')),
('1',_('EXECUTED')),
)
imp_date = models.DateField(_('date'),blank=True,null=True,default=datetime.datetime.today)
title = models.CharField(_('title'),max_length=const.DB_CHAR_NAME_40)
description = models.TextField(_('description'),blank=True,null=True)
content_type = models.ForeignKey(ContentType,verbose_name=_("content type"),limit_choices_to={"app_label__in":['basedata','organ','auth']})
attach = models.FileField(_('attach'),blank=True,null=True,upload_to='data')
is_clear = models.BooleanField(_('clear old data?'),default=0)
handler = models.CharField(_('handler class'),max_length=const.DB_CHAR_NAME_80,blank=True,null=True)
status = models.CharField(_('status'),max_length=const.DB_CHAR_CODE_2,default='0',choices=STATUS)
def action_import(self,request):
from django.db import transaction
if self.attach:
if self.handler:
klass = ExcelManager().handlers.get(self.handler)
with transaction.atomic():
klass.handle(self,self.attach)
self.status = 1
self.save()
else:
import xlrd
import os
from mis import settings
path = os.path.join(settings.MEDIA_ROOT,self.attach.name)
workbook = xlrd.open_workbook(path)
sheet = workbook.sheet_by_index(0)
row_count = sheet.nrows
col_count = sheet.ncols
cols = []
with transaction.atomic():
for row_index in range(row_count):
line = sheet.row_values(row_index)
if row_index == 0:
cols = line
continue
elif row_index == 1:
continue
else:
klass = self.content_type.model_class()
values = line
params = {}
for name in cols:
index = cols.index(name)
v = values[index]
if type(v) == str:
v = force_text(v.decode('gbk'))
params[name]=v
# print 'name is %s value is %s'%(name,v)
try:
params.pop('')
except Exception,e:
pass
# print params
klass.objects.create(**params)
self.status = '1'
self.save()
class Meta:
verbose_name = _("data import")
verbose_name_plural = _("data import")
class Document(generic.BO):
"""
文档管理
"""
TP = (
('00',_('SYSTEM MANUAL')),
('10',_('BUSINESS DOC')),
)
STATUS = (
('0',_('draft')),
('1',_('published'))
)
index_weight = 8
code = models.CharField(_('code'),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
title = models.CharField(_('title'),max_length=const.DB_CHAR_NAME_120)
keywords = models.CharField(_('keywords'),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
description = models.TextField(_('description'),blank=True,null=True)
tp = models.CharField(_('type'),max_length=const.DB_CHAR_CODE_2,default='10',choices=TP)
business_domain = models.CharField(_("business domain"),max_length=const.DB_CHAR_CODE_4,choices=const.get_value_list('S045'),default='OT')
user = models.ForeignKey(User,verbose_name=_('user'),blank=True,null=True)
status = models.CharField(_('status'),max_length=const.DB_CHAR_CODE_2,default='0',choices=STATUS)
pub_date = models.DateTimeField(_('publish date'),blank=True,null=True)
size = models.CharField(_('size'),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
attach = models.FileField(_('attach'),blank=True,null=True,upload_to='doc')
class Meta:
verbose_name = _("document")
verbose_name_plural = _("documents")
+3
View File
@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.
+6
View File
@@ -0,0 +1,6 @@
from django.conf.urls import include, url,static
import basedata.views
urlpatterns = [
url(r"dataimport/(?P<object_id>\d+)/action", basedata.views.action_import),
]
+42
View File
@@ -0,0 +1,42 @@
# coding=utf-8
from django.contrib.admin import site
from django.http.response import HttpResponseRedirect
from django.utils.encoding import force_text
from django.template.response import TemplateResponse
from django.contrib import messages
from basedata.models import DataImport
from django.utils.translation import ugettext_lazy as _
def action_import(request,object_id):
"""
数据导入操作
:param request:
:param object_id:
:return:
"""
title = _("Are you sure?")
obj = DataImport.objects.get(id=int(object_id))
opts = obj._meta
objects_name = force_text(opts.verbose_name)
if request.POST.get("post"):
obj.action_import(request)
try:
messages.success(request,_('data import successfully'))
except Exception,e:
messages.error(request,e)
return HttpResponseRedirect("/admin/basedata/dataimport/%s"%(object_id))
context = dict(
site.each_context(request),
title=title,
opts=opts,
objects_name=objects_name,
object=obj,
)
request.current_app = site.name
return TemplateResponse(request,'admin/invent/stockin/in_confirmation.html', context)
+1
View File
@@ -0,0 +1 @@
__author__ = 'Administrator'
+39
View File
@@ -0,0 +1,39 @@
# coding=utf-8
from django.db import connection
from django.utils.translation import ugettext_lazy as _
__author__ = 'zhugl'
DB_CHAR_CODE_2 = 2
DB_CHAR_CODE_4 = 4
DB_CHAR_CODE_6 = 6
DB_CHAR_CODE_8 = 8
DB_CHAR_CODE_10 = 10
DB_CHAR_NAME_20 = 20
DB_CHAR_NAME_40 = 40
DB_CHAR_NAME_60 = 60
DB_CHAR_NAME_80 = 80
DB_CHAR_NAME_120 = 120
DB_CHAR_NAME_200 = 200
STATUS_ON_OFF = (
(0,_('OFF')),
(0,_('ON')),
)
def get_value_list(group):
"""
获取值列表信息
"""
if group:
try:
cursor = connection.cursor()
cursor.execute('SELECT code,name FROM basedata_valuelistitem WHERE group_code=%s AND status=1',[group])
rows = cursor.fetchall()
return tuple([(code,name) for code,name in rows])
except Exception,e:
return None
else:
return None
+312
View File
@@ -0,0 +1,312 @@
# coding=utf-8
__author__ = 'zhugl'
# created at 15-4-21
import datetime
import xlwt
import re
from django.db import models
from django.db import connection,transaction
from django.db.models import fields
from django.db.models.fields import related
from django.contrib import admin
from django.http import HttpRequest,HttpResponseRedirect,HttpResponse
from django.contrib.contenttypes.models import ContentType
from django.utils.text import force_text
from django.utils.encoding import smart_str
from django.utils.http import urlquote
from django.utils.translation import ugettext_lazy as _
from common import const
from midware import cuser
def update(sql, params=None):
"""
:param sql:
:param params:
:return:
"""
cursor = connection.cursor()
with transaction.atomic():
try:
if params:
cursor.execute(sql,params)
else:
cursor.execute(sql)
except Exception,e:
print e
def get_app_model_info_from_request(request):
"""
"""
if request and isinstance(request,HttpRequest):
import re
pattern = re.compile(r"/(admin)/(\w+)/(\w+)/(\d+)")
match = pattern.match(request.path)
if match and match.group():
app = match.group(2)
model = match.group(3)
oid = match.group(4)
ct = ContentType.objects.get(app_label=app,model=model)
obj = ct.get_object_for_this_type(id=oid)
return {'app':app,'model':model,'id':oid,'obj':obj}
return None
class MineBOManager(models.Manager):
"""
get the objects created by current login user
"""
def get_query_set(self):
return super(MineBOManager,self).get_query_set().filter(creator=cuser.getuser())
class BOManager(models.Manager):
"""
"""
def get_query_set(self):
return super(BOManager,self).get_query_set()
class BO(models.Model):
"""
All business object derive from this class
"""
begin = models.DateField(_('begin date'),blank=True,null=True)
end = models.DateField(_('end date'),blank=True,null=True)
creator = models.CharField(_("creator"),blank=True,null=True,max_length=const.DB_CHAR_NAME_20)
modifier = models.CharField(_("modifier"),blank=True,null=True,max_length=const.DB_CHAR_NAME_20)
creation = models.DateTimeField(_('creation'),auto_now_add=True,blank=True,null=True)
modification = models.DateTimeField(_('modification'),auto_now=True,blank=True,null=True)
# mine = MineBOManager()
objects = models.Manager()
def __unicode__(self):
display = getattr(self,'name',None) or getattr(self,'title',None) or getattr(self,'description',None)
if not display:
display = ' '
return u'%s' % display
class Meta:
abstract = True
class BOAdmin(admin.ModelAdmin):
"""
All business object admin derive from this class
"""
CODE_NUMBER_WIDTH = 4
CODE_PREFIX = '9'
extra_buttons = []
exclude = ['creator','modifier','creation','modification','begin','end']
list_per_page = 18
actions = ['export_selected_data']
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
"""
:param request:
:param object_id:
:param form_url:
:param extra_context:
:return:
"""
app_info = get_app_model_info_from_request(request)
workflow_modal = None
workflow_instance = None
has_workflow_modal = False
has_workflow_instance = False
show_workflow_line = False
show_submit_button = False
can_restart = False
can_edit = False
# print app_info
if app_info:
try:
modal = ContentType.objects.get(app_label='workflow',model='modal')
workflow_modal = modal.get_object_for_this_type(app_name=app_info['app'],model_name=app_info['model'])
has_workflow_modal = True
# print workflow_modal.code
instance = ContentType.objects.get(app_label='workflow',model='instance')
workflow_instance = instance.get_object_for_this_type(modal=workflow_modal,object_id=app_info['id'])
has_workflow_instance = True
todo = ContentType.objects.get(app_label='workflow',model='todolist')
todo_list = todo.model_class().objects.filter(inst=workflow_instance,status=0,user=request.user)
x = todo_list.all()
if x and len(x)>0:
can_edit = x[0].node.can_edit
if todo_list.count() > 0:
# print 'we fount it'
unread = todo_list.filter(is_read=0)
show_workflow_line = True
if unread.count() > 0:
unread.update(is_read=1,read_time=datetime.datetime.now())
if workflow_instance.status == 3 and request.user == workflow_instance.starter:
can_restart = True
show_workflow_line = True
except Exception,e:
print Exception,e
if workflow_modal and not workflow_instance:
show_submit_button = True
extra_context = extra_context or {}
ctx = dict(
has_workflow_instance = has_workflow_instance,
has_workflow_modal = has_workflow_modal,
workflow_modal = workflow_modal,
workflow_instance = workflow_instance,
show_workflow_line = show_workflow_line,
can_restart = can_restart,
can_edit = can_edit,
show_submit_button = show_submit_button,
)
if len(self.extra_buttons) > 0:
buttons = dict(
extra_buttons = self.extra_buttons
)
ctx.update(buttons)
extra_context.update(ctx)
# print extra_context
return super(BOAdmin,self).changeform_view(request,object_id,form_url,extra_context)
def history_view(self, request, object_id, extra_context=None):
"""
:param request:
:param object_id:
:param extra_context:
:return:
"""
app_info = get_app_model_info_from_request(request)
# print app_info
if app_info:
try:
modal = ContentType.objects.get(app_label='workflow',model='modal')
workflow_modal = modal.get_object_for_this_type(app_name=app_info['app'],model_name=app_info['model'])
has_workflow_modal = True
instance = ContentType.objects.get(app_label='workflow',model='instance')
workflow_instance = instance.get_object_for_this_type(modal=workflow_modal,object_id=app_info['id'])
has_workflow_instance = True
history = ContentType.objects.get(app_label='workflow',model='history')
history_list = history.model_class().objects.filter(inst=workflow_instance)
has_history = True
todo = ContentType.objects.get(app_label='workflow',model='todolist')
todo_list = todo.model_class().objects.filter(inst=workflow_instance,status=0).exclude(node=None)
extra_context = extra_context or {}
ctx = dict(
has_workflow_instance = has_workflow_instance,
has_workflow_modal = has_workflow_modal,
workflow_modal = workflow_modal,
workflow_instance = workflow_instance,
history_list = history_list,
has_history = has_history,
todo_list = todo_list,
)
# print history_list
extra_context.update(ctx)
except Exception,e:
print Exception,e
pass
return super(BOAdmin,self).history_view(request,object_id,extra_context)
def get_changeform_initial_data(self, request):
import datetime
return {'begin':datetime.date.today,'end':datetime.date(9999,12,31)}
def save_model(self, request, obj, form, change):
if change:
setattr(obj,'modifier',request.user.username)
else:
setattr(obj,'creator',request.user.username)
setattr(obj,'begin',datetime.date.today())
setattr(obj,'end',datetime.date(9999,12,31))
try:
setattr(obj,'user',request.user)
except Exception,e:
pass
super(BOAdmin,self).save_model(request,obj,form,change)
# print '=========it is here========='
try:
code = getattr(obj,'code')
# print code
if code is None or len(code) == 0:
fmt = '%s%0'+str(self.CODE_NUMBER_WIDTH)+'d'
code = fmt % (self.CODE_PREFIX,obj.id)
table = obj._meta.db_table
sql = 'update %s set code = \'%s\' where id=%s' % (table,code,obj.id)
print sql
update(sql)
except Exception,e:
print e
# def response_change(self, request, obj):
# return HttpResponseRedirect('')
def export_selected_data(self,request,queryset):
ops = self.model._meta
workbook = xlwt.Workbook(encoding='utf-8')
dd = datetime.date.today().strftime('%Y%m%d')
file_name = force_text(ops.verbose_name+dd)
sheet = workbook.add_sheet(force_text(ops.verbose_name))
obj_fields = getattr(self,'export_fields',None) or self.list_display or self.fields
head_col_index = 0
for field in obj_fields:
col_name = field
try:
f = ops.get_field(field)
col_name = f.verbose_name
except Exception,e:
f = getattr(self.model,field)
if hasattr(f,'short_description'):
col_name = f.short_description
sheet.write(0,head_col_index,force_text(col_name))
head_col_index+=1
row_index = 1
for obj in queryset:
col_index = 0
for field in obj_fields:
f = field
try:
f = ops.get_field(field)
except Exception,e:
pass
v = getattr(obj,field,'')
if hasattr(v,'__call__') or callable(v):
v = v()
elif type(f) == fields.DateField:
v = v.strftime('%Y-%m-%d')
elif type(f) == fields.DateTimeField:
v = v.strftime('%Y-%m-%d %H:%M')
elif type(f) == fields.CharField and f.choices:
fc = 'get_'+field+'_display'
v = getattr(obj,fc)()
elif type(f) == related.ForeignKey:
v = str(v)
sheet.write(row_index,col_index,v)
col_index += 1
row_index += 1
response = HttpResponse(content_type='application/vnd.ms-excel')
agent = request.META.get('HTTP_USER_AGENT')
nn = smart_str(file_name)
if agent and re.search('MSIE',agent):
nn = urlquote(file_name)
response['Content-Disposition'] = 'attachment; filename=%s.xls'%nn
workbook.save(response)
return response
#self.message_user(request,'SUCCESS')
export_selected_data.short_description = _("export selected %(verbose_name_plural)s")
class Meta:
ordering = ['-creation']
class Media:
css = {'all':('css/maximus.css',)}
js = ('js/maximus.js',)
+30
View File
@@ -0,0 +1,30 @@
version: '3'
services:
web:
build: .
volumes:
- .:/app
ports:
- "6010:8000"
depends_on:
- db
environment:
- DATABASE_HOST=db
- DATABASE_NAME=mis
- DATABASE_USER=root
- DATABASE_PASSWORD=root
db:
image: mysql:5.7
command: --default-authentication-plugin=mysql_native_password
volumes:
- mysql_data:/var/lib/mysql
- ./Install/SQL:/docker-entrypoint-initdb.d
environment:
- MYSQL_DATABASE=mis
- MYSQL_ROOT_PASSWORD=root
- MYSQL_PASSWORD=root
volumes:
mysql_data:
+32
View File
@@ -0,0 +1,32 @@
version: '3'
services:
web:
build: .
volumes:
- .:/app
ports:
- "6010:8000"
depends_on:
- db
environment:
- DATABASE_HOST=db
- DATABASE_NAME=mis
- DATABASE_USER=root
- DATABASE_PASSWORD=root
db:
image: mysql:5.7
command: --default-authentication-plugin=mysql_native_password
volumes:
- mysql_data:/var/lib/mysql
- ./Install/SQL:/docker-entrypoint-initdb.d
environment:
- MYSQL_DATABASE=mis
- MYSQL_ROOT_PASSWORD=root
- MYSQL_PASSWORD=root
ports:
- "6011:3306"
volumes:
mysql_data:
+1
View File
@@ -0,0 +1 @@
default_app_config = 'hr.apps.MyAppConfig'
+47
View File
@@ -0,0 +1,47 @@
# coding = utf-8
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from common import generic
from common import const
from hr.models import Entry,SalaryItem,EmployeeSalaryItem
class SalaryItemAdmin(admin.ModelAdmin):
list_display = ['code','classification','name','plus_or_minus','required']
list_display_links = ['code','name']
list_per_page = 20
class EmployeeSalaryItemInline(admin.TabularInline):
model = EmployeeSalaryItem
exclude = ['employee']
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
if db_field.name == 'salary_item':
kwargs['queryset'] = SalaryItem.objects.filter(required=1)
return super(EmployeeSalaryItemInline,self).formfield_for_foreignkey(db_field,request,**kwargs)
def get_extra(self, request, obj=None, **kwargs):
if obj:
return 0
else:
return 3
class EntryAdmin(generic.BOAdmin):
list_display = ['code','name','gender','position','rank','probation_months','probation_end']
inlines = [EmployeeSalaryItemInline]
fieldsets = [
(None,{'fields':[('code','position',),('name','pinyin',),('address','zipcode',),('idcard','phone',),('memo',),('profile',)]}),
(_('org distribute'),{'fields':[('guider',),('rank','ygxs',),('category','probation_months',),('probation_begin','probation_end',)],'classes': ['collapse']})
]
raw_id_fields = ['position','guider']
def get_changeform_initial_data(self, request):
import datetime
end = datetime.date.today()+datetime.timedelta(90)
return {'probation_end':end}
admin.site.register(SalaryItem,SalaryItemAdmin)
admin.site.register(Entry,EntryAdmin)
+11
View File
@@ -0,0 +1,11 @@
# created at 15-5-23
# coding=utf-8
__author__ = 'zhugl'
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class MyAppConfig(AppConfig):
name = 'hr'
verbose_name = _("human resource")
+125
View File
@@ -0,0 +1,125 @@
# coding=utf-8
from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from common import generic
from common import const
from basedata.models import Position,Employee
from organ.models import OrgUnit
import datetime
class SalaryItemHandler:
code = None
def __init__(self,employee):
self.employee = employee
def value(self):
return 0
class SalaryItem(models.Model):
"""
工资项
"""
formulas = {}
@classmethod
def add_formula(cls, code, handler):
SalaryItem.formulas[code] = handler
@classmethod
def get_formula(cls):
return SalaryItem.formulas.get(cls.code,None)
code = models.CharField(_("code"),max_length=const.DB_CHAR_CODE_10,null=True)
name = models.CharField(_("name"),max_length=const.DB_CHAR_NAME_120)
classification = models.CharField(_("classification"),max_length=const.DB_CHAR_CODE_2,choices=const.get_value_list('S048'),default='10')
plus_or_minus = models.CharField(_("plus or minus"),max_length=const.DB_CHAR_CODE_2,choices=const.get_value_list('S049'),default='+')
required = models.BooleanField(_("is required"),default=0)
def __unicode__(self):
return "%s %s" % (self.code,self.name)
class Meta:
verbose_name = _('salary item')
verbose_name_plural = _('salary items')
ordering = ('code',)
class Entry(generic.BO):
"""
人员入职
"""
code = models.CharField(_("employee number"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
name = models.CharField(_("employee name"),max_length=const.DB_CHAR_NAME_120)
pinyin = models.CharField(_("pinyin"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
birthday = models.DateField(_("birthday"),blank=True,null=True)
gender = models.CharField(_("gender"),max_length=const.DB_CHAR_CODE_2,choices=const.get_value_list('gender'),default='1')
idcard = models.CharField(_("id card"),max_length=const.DB_CHAR_NAME_20)
address = models.CharField(_("mail address"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
zipcode = models.CharField(_("zipcode"),max_length=const.DB_CHAR_CODE_8)
phone = models.CharField(_("phone"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
guider = models.ForeignKey(Employee,verbose_name=_("guider"))
position = models.ForeignKey(Position,verbose_name = _('designate position'))
rank = models.CharField(_("employee rank"),max_length=const.DB_CHAR_CODE_2,default='00',choices=const.get_value_list('S017'))
ygxs = models.CharField(_("employ ygxs"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S019'),default='2')
category = models.CharField(_("employ category"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=const.get_value_list('S018'),default='21')
probation_months = models.CharField(_("probation months"),max_length=2,default='3',choices=const.get_value_list('S047'))
probation_begin = models.DateField(_("probation begin"),default=datetime.date.today)
probation_end = models.DateField(_("probation end"),blank=True,null=True)
memo = models.TextField(_("memo"),blank=True,null=True)
profile = models.FileField(_("profile"),blank=True,null=True,upload_to='hr profile')
class Meta:
verbose_name = _("employee entry")
verbose_name_plural = _("employee entries")
permissions = (
('modify_salary_item',_("modify salary item")),
)
class EmployeeSalaryItem(models.Model):
"""
"""
entry = models.ForeignKey(Entry,verbose_name=_("employee entry"))
employee = models.ForeignKey(Employee,verbose_name=_("employee"),blank=True,null=True)
salary_item = models.ForeignKey(SalaryItem,verbose_name=_("salary item"))
calculate_way = models.CharField(_("calculate way"),max_length=const.DB_CHAR_CODE_2,choices=const.get_value_list('S050'),default='10')
fixed_value = models.DecimalField(_("fixed value"),blank=True,null=True,max_digits=10,decimal_places=2)
base_value = models.DecimalField(_("base value"),blank=True,null=True,max_digits=10,decimal_places=2)
org_percent = models.DecimalField(_("org percent"),blank=True,null=True,max_digits=4,decimal_places=2)
employee_percent = models.DecimalField(_("employee percent"),blank=True,null=True,max_digits=4,decimal_places=2)
class Meta:
verbose_name = _("salary item")
verbose_name_plural = _("salary item")
unique_together = (('entry', 'salary_item'),)
class Transfer(generic.BO):
"""
人员调动
"""
employee = models.ForeignKey(Employee,verbose_name=_("employee"))
class Meta:
verbose_name = _("employee transfer")
verbose_name_plural = _("employee transfers")
class Departure(generic.BO):
"""
人员离职
"""
employee = models.ForeignKey(Employee,verbose_name=_("employee"))
class Meta:
verbose_name = _("employee departure")
verbose_name_plural = _("employee departures")
+3
View File
@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.
+3
View File
@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.
Binary file not shown.

After

Width:  |  Height:  |  Size: 914 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

+1
View File
@@ -0,0 +1 @@
default_app_config = "invent.apps.MyAppConfig"
+248
View File
@@ -0,0 +1,248 @@
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from common import generic
from basedata.models import Material
from invent.models import StockIn,StockOut,InitialInventory,InItem,OutItem,InitItem,Inventory,InItemForm,InOutDetail,\
WareReturn,ReturnItem,WareAdjust,AdjustItem
class InItemInline(admin.TabularInline):
model = InItem
form = InItemForm
fields = ('material', 'measure', 'cnt', 'price')
raw_id_fields = ['material']
def get_extra(self, request, obj=None, **kwargs):
if obj:
return 0
else:
return 1
def get_readonly_fields(self, request, obj=None):
if obj and obj.status == 1:
return ['material', 'measure', 'cnt', 'price']
else:
return []
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
if db_field.name == 'material':
kwargs['queryset'] = Material.objects.filter(is_virtual=0)
return super(InItemInline,self).formfield_for_foreignkey(db_field,request,**kwargs)
class OutItemInline(admin.TabularInline):
model = OutItem
fields = ('inventory', 'measure', 'cnt', 'price','warehouse',)
raw_id_fields = ['inventory']
readonly_fields = ['measure', 'price', 'warehouse']
def get_extra(self, request, obj=None, **kwargs):
if obj:
return 0
else:
return 3
class InitItemInline(admin.TabularInline):
model = InitItem
fields = ('material', 'measure', 'cnt', 'warehouse', 'price',)
raw_id_fields = ['material']
def get_readonly_fields(self, request, obj=None):
if obj and obj.execute_time:
return ['material', 'measure', 'cnt', 'warehouse', 'price']
else:
return []
def get_extra(self, request, obj=None, **kwargs):
if obj:
return 0
else:
return 3
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
if db_field.name == 'material':
kwargs['queryset'] = Material.objects.filter(is_virtual=0)
return super(InitItemInline,self).formfield_for_foreignkey(db_field,request,**kwargs)
class StockInAdmin(generic.BOAdmin):
CODE_PREFIX = 'RK'
CODE_NUMBER_WIDTH = 5
list_display = ['code','title','money_of_amount','status','entry_time']
inlines = [InItemInline]
raw_id_fields = ['po']
fields = (
('code',),('title',),('po',),('warehouse',),('batch',),('status','amount',)
)
date_hierarchy = 'begin'
extra_buttons = [{'href':'cin','title':_('Action Stock In')}]
def save_model(self, request, obj, form, change):
import decimal
super(StockInAdmin,self).save_model(request,obj,form,change)
if obj and obj.po:
po_items = obj.po.poitem_set.filter(left_cnt__gt=0).all()
for item in po_items:
try:
InItem.objects.get(po_item=item,master=obj)
continue
except Exception,e:
pp = item.discount_price or item.price
if decimal.Decimal(item.tax) > decimal.Decimal(0):
pp = pp /(decimal.Decimal(1)+decimal.Decimal(item.tax))
InItem.objects.create(warehouse=obj.warehouse,material=item.material,measure=item.measure,prop='+',
po_item=item,master=obj,cnt=item.left_cnt,price=pp,batch=obj.batch,source=obj.code)
def get_readonly_fields(self, request, obj=None):
print obj
if obj and obj.status == 9:
return ['code','title','po','warehouse','batch','status']
else:
return ['status','amount']
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
if object_id:
obj = StockIn.objects.get(id=object_id)
if obj and obj.execute_time:
extra_context.update(dict(readonly=True))
return super(StockInAdmin,self).changeform_view(request,object_id,form_url,extra_context)
class StockOutAdmin(generic.BOAdmin):
CODE_PREFIX = 'CK'
CODE_NUMBER_WIDTH = 5
list_display = ['code','title','project','status','out_time','out_amount']
list_display_links = ['code','title']
date_hierarchy = 'begin'
inlines = [OutItemInline]
raw_id_fields = ['project','wo','user']
fields = (
('code', 'status',),('project', ),('wo','user'),
('title','amount',),('description',),
)
readonly_fields = ['status']
extra_buttons = [{'href':'out','title':_('Action Stock Out')}]
search_fields = ['code','title','user__username']
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
if object_id:
obj = StockOut.objects.get(id=object_id)
if obj and obj.execute_time:
extra_context.update(dict(readonly=True))
return super(StockOutAdmin,self).changeform_view(request,object_id,form_url,extra_context)
def save_model(self, request, obj, form, change):
if obj and obj.user is None:
obj.user = request.user
super(StockOutAdmin,self).save_model(request,obj,form,change)
class InitialInventoryAdmin(generic.BOAdmin):
CODE_PREFIX = 'QC'
CODE_NUMBER_WIDTH = 3
list_display = ['code','title','status']
inlines = [InitItemInline]
fields = ('code','title','org','status','amount','attach')
readonly_fields = ['status','amount']
extra_buttons = [{'href':'cin','title':_('Action Stock In')}]
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
if object_id:
obj = InitialInventory.objects.get(id=object_id)
if obj and obj.execute_time:
extra_context.update(dict(readonly=True))
return super(InitialInventoryAdmin,self).changeform_view(request,object_id,form_url,extra_context)
class InventoryAdmin(generic.BOAdmin):
list_display = ['material','measure','warehouse','cnt','price']
search_fields = ['material__name','material__code']
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context.update(dict(readonly=True))
if object_id:
inventory = Inventory.objects.get(id=object_id)
material = inventory.material
warehouse = inventory.warehouse
detail = InOutDetail.objects.filter(material=material,warehouse=warehouse)
extra_context.update(dict(detail=detail))
return super(InventoryAdmin,self).changeform_view(request,object_id,form_url,extra_context)
class ReturnItemInline(admin.TabularInline):
model = ReturnItem
fields = ['material','measure','warehouse','out_cnt','cnt']
readonly_fields = ['material','measure','warehouse','out_cnt']
extra = 0
class WareReturnAdmin(generic.BOAdmin):
"""
"""
CODE_PREFIX = 'FK'
CODE_NUMBER_WIDTH = 4
list_display = ['code','title','out']
fields = (
('code',),('title',),('out',),('amount',),('status',)
)
readonly_fields = ['status']
raw_id_fields = ['out']
inlines = [ReturnItemInline]
extra_buttons = [{'href':'cin','title':_('Action Ware Return')}]
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
if object_id :
obj = WareReturn.objects.get(id=object_id)
if obj.status == '9':
extra_context = extra_context or {}
extra_context.update(dict(readonly=True))
return super(WareReturnAdmin,self).changeform_view(request,object_id,form_url,extra_context)
class AdjustItemInline(admin.TabularInline):
model = AdjustItem
fields = ['inventory','measure','warehouse','prop','cnt']
readonly_fields = ['measure','warehouse']
raw_id_fields = ['inventory']
def get_extra(self, request, obj=None, **kwargs):
if obj:
return 0
else:
return 1
class WareAdjustAdmin(generic.BOAdmin):
CODE_PREFIX = 'TZ'
CODE_NUMBER_WIDTH = 3
list_display = ['code','title','status']
fields = (
('code',),('title',),('description',),('status',)
)
readonly_fields = ['status']
inlines = [AdjustItemInline]
extra_buttons = [{'href':'adjust','title':_('Action Ware Adjust')}]
date_hierarchy = 'begin'
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
if object_id:
obj = WareAdjust.objects.get(id=object_id)
if obj and obj.status == '9':
extra_context = extra_context or {}
extra_context.update(dict(readonly=True))
return super(WareAdjustAdmin,self).changeform_view(request,object_id,form_url,extra_context)
admin.site.register(StockIn,StockInAdmin)
admin.site.register(StockOut,StockOutAdmin)
admin.site.register(InitialInventory,InitialInventoryAdmin)
admin.site.register(Inventory,InventoryAdmin)
admin.site.register(WareReturn,WareReturnAdmin)
admin.site.register(WareAdjust,WareAdjustAdmin)
+11
View File
@@ -0,0 +1,11 @@
# created at 15-5-23
# coding=utf-8
__author__ = 'zhugl'
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class MyAppConfig(AppConfig):
name = 'invent'
verbose_name = _("inventory manage")
+493
View File
@@ -0,0 +1,493 @@
# coding=utf-8
import csv
import os
import datetime
import decimal
from django.db import transaction
from django.db import models
from django import forms
from mis import settings
from django.utils.text import force_text
from django.db import transaction
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from common import generic
from common import const
from basedata.models import Material,Warehouse,Measure,Organization,Project
from selfhelp.models import WorkOrder
from purchase.models import PurchaseOrder,POItem
class Inventory(generic.BO):
"""
库存信息
"""
index_weight = 1
org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True)
warehouse = models.ForeignKey(Warehouse,verbose_name=_("warehouse"))
material = models.ForeignKey(Material,verbose_name=_("material"))
measure = models.ForeignKey(Measure,verbose_name=_("measure"))
cnt = models.DecimalField(_("count"),max_digits=14,decimal_places=4)
batch = models.CharField(_("batch"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
price = models.DecimalField(_("price"),max_digits=14,decimal_places=4)
def __unicode__(self):
des = self.material.spec or ''
return u'%s %s %s' % (self.material.code,self.material.name,des)
class Meta:
verbose_name = _("Inventory")
verbose_name_plural = _("Inventory")
ordering = ['material']
class InitialInventory(generic.BO):
"""
期初库存
"""
STATUS = (
('0', _("NEW")),
('9', _("EXECUTED"))
)
index_weight = 9
code = models.CharField(_("code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True)
title = models.CharField(_("title"),max_length=const.DB_CHAR_NAME_40)
user = models.ForeignKey(User,verbose_name=_("user"),blank=True,null=True)
status = models.CharField(_("status"),max_length=const.DB_CHAR_CODE_2,default='0',choices=STATUS)
execute_time = models.DateTimeField(_("execute time"),blank=True,null=True)
attach = models.FileField(_('attach'),blank=True,null=True,upload_to='inventory',help_text=u'参考FD0002模板文档')
amount = models.DecimalField(_('money of amount'),max_digits=14,decimal_places=4,blank=True,null=True)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
super(InitialInventory,self).save(force_insert,force_update,using,update_fields)
count = InitItem.objects.filter(master=self).count()
if self.attach and count == 0:
path = os.path.join(settings.MEDIA_ROOT,self.attach.name)
reader = csv.reader(open(path,'r'))
index = 0
with transaction.atomic():
for code,name,description,unit_code,unit_name,warehouse_code,warehouse_name,price,cnt in reader:
index+=1
if index == 1:
continue
material = None
measure = None
house = None
try:
measure = Measure.objects.get(code=unit_code)
except Exception,e:
measure = Measure.objects.create(code=unit_code,name=force_text(unit_name.decode('gbk')))
try:
house = Warehouse.objects.get(code=warehouse_code)
except Exception,e:
house = Warehouse.objects.create(code=warehouse_code,name=force_text(warehouse_name.decode('gbk')))
try:
material = Material.objects.get(code=code)
except Exception,e:
material = Material(code=code,name=force_text(name.decode('gbk')),spec=force_text(description.decode('gbk')),warehouse=house)
material.stock_price = price
material.save()
InitItem.objects.create(master=self,material=material,price=price,cnt=cnt,warehouse=house,measure=measure,source=self.code)
def init_entry(self,request=None):
"""
执行期初入库
"""
count = InitItem.objects.filter(master=self).count()
if count > 0:
with transaction.atomic():
total_amount = 0
for item in InitItem.objects.filter(master=self).all():
Inventory.objects.create(material=item.material,price=item.price,cnt=item.cnt,warehouse=item.warehouse,measure=item.measure)
item.status = 1
item.event_time = datetime.datetime.now()
item.source = self.code
item.save()
item.material.stock_price = item.price
if item.material.measure.count() == 0:
item.material.measure.add(item.measure)
item.material.save()
total_amount += item.price*item.cnt
self.amount = total_amount
self.status = '9'
self.execute_time = datetime.datetime.now()
self.save()
else:
raise Exception(_("none material was found"))
class Meta:
verbose_name = _("Initial Inventory")
verbose_name_plural = _("Initial Inventory")
class StockIn(generic.BO):
"""
入库单
"""
STATUS = (
('0', _("NEW")),
('1', _("QUALITY TESTING")),
('9', _("EXECUTED"))
)
index_weight = 3
code = models.CharField(_("code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True)
warehouse = models.ForeignKey(Warehouse,verbose_name=_("warehouse"))
title = models.CharField(_("title"),max_length=const.DB_CHAR_NAME_40)
user = models.ForeignKey(User,verbose_name=_("user"),blank=True,null=True)
status = models.CharField(_("status"),max_length=const.DB_CHAR_CODE_2,default='0',choices=STATUS)
execute_time = models.DateTimeField(_("execute time"),blank=True,null=True)
po = models.ForeignKey(PurchaseOrder,verbose_name=_("purchase order"),null=True,limit_choices_to={"entry_status":"0"},blank=True)
amount = models.DecimalField(_("stock in money of amount"),max_digits=14,decimal_places=4,blank=True,null=True)
batch = models.CharField(_("batch"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
def money_of_amount(self):
if self.amount:
return self.amount
else:
return 0.00
def entry_time(self):
if self.execute_time:
return self.execute_time
else:
return ''
money_of_amount.short_description = _("stock in money of amount")
entry_time.short_description = _("entry time")
def action_entry(self,request):
"""
执行入库操作
"""
if self.initem_set.count() > 0:
with transaction.atomic():
total_amount = decimal.Decimal(0)
for item in self.initem_set.filter(status=0).all():
try:
inventory = Inventory.objects.get(warehouse=self.warehouse,material=item.material,measure=item.measure)
if inventory.price != item.price:
at = decimal.Decimal(inventory.price*inventory.cnt+item.price*item.cnt)
ac = decimal.Decimal(inventory.cnt+item.cnt)
average = at/ac
inventory.price = average
item.material.stock_price = average
item.material.save()
inventory.cnt += item.cnt
inventory.save()
total_amount += item.price*item.cnt
except Exception,e:
Inventory.objects.create(warehouse=self.warehouse,material=item.material,measure=item.measure,
cnt=item.cnt,price=item.price,org=self.org)
item.material.stock_price = item.price
item.material.save()
item.status=1
item.event_time = datetime.datetime.now()
item.source = self.code
item.save()
total_amount += item.price*item.cnt
# saving the purchase item
item.po_item.is_in_stock = 1
item.po_item.in_stock_time = datetime.datetime.now()
item.po_item.entry_cnt = item.cnt
item.po_item.save()
none_zero_left = POItem.objects.filter(po=item.po_item.po,left_cnt__gt=0).count()
if none_zero_left == 0:
item.po_item.po.status = '99'
item.po_item.po.entry_status = 1
item.po_item.po.entry_time = datetime.datetime.now()
item.po_item.po.save()
# saving stock in item status
item.status = 1
item.event_time = datetime.datetime.now()
# updating master's record
self.status = 9
self.execute_time = datetime.datetime.now()
self.amount = total_amount
self.save()
class Meta:
verbose_name = _("StockIn")
verbose_name_plural = _("StockIn")
class StockOut(generic.BO):
"""
领料单
"""
STATUS = (
('0', _("NEW")),
('1', _("IN PROGRESS")),
('9', _("EXECUTED"))
)
index_weight = 2
code = models.CharField(_("code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True)
title = models.CharField(_("title"),max_length=const.DB_CHAR_NAME_40)
project = models.ForeignKey(Project,verbose_name=_("project"),blank=True,null=True)
wo = models.ForeignKey(WorkOrder,verbose_name=_("work order"),blank=True,null=True)
description = models.TextField(_("description"),blank=True,null=True)
amount = models.DecimalField(_("money of amount"),max_digits=14,decimal_places=4,blank=True,null=True)
user = models.ForeignKey(User,verbose_name=_("out user"),blank=True,null=True)
status = models.CharField(_("status"),max_length=const.DB_CHAR_CODE_2,default='0',choices=STATUS)
execute_time = models.DateTimeField(_("execute time"),blank=True,null=True)
def out_amount(self):
return self.amount or ''
def out_time(self):
return self.execute_time or ''
out_time.short_description = _('stock out time')
out_amount.short_description = _('stock out amount')
def action_out(self,request=None):
"""
执行出库操作
"""
if self.outitem_set.count() > 0:
with transaction.atomic():
total = decimal.Decimal(0)
for item in OutItem.objects.filter(master=self).all():
if item.inventory.cnt < item.cnt:
raise Exception('%s does not meets required' % item.material)
if item.cnt:
total += item.cnt * item.inventory.price
item.inventory.cnt -= item.cnt
item.status = 1
item.price = item.inventory.price
item.event_time = datetime.datetime.now()
item.inventory.save()
item.source=self.code
item.save()
if total > 0:
self.amount = total
self.status = '9'
self.execute_time = datetime.datetime.now()
self.save()
class Meta:
verbose_name = _("StockOut")
verbose_name_plural = _("StockOut")
class WareReturn(generic.BO):
"""
返库单
"""
STATUS = (
('0', _("NEW")),
('1', _("IN PROGRESS")),
('9', _("EXECUTED"))
)
index_weight = 5
code = models.CharField(_("code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True)
title = models.CharField(_("title"),max_length=const.DB_CHAR_NAME_40)
out = models.ForeignKey(StockOut,verbose_name=_('StockOut'))
amount = models.DecimalField(_("money of amount"),max_digits=14,decimal_places=4,blank=True,null=True)
user = models.ForeignKey(User,verbose_name=_("out user"),blank=True,null=True)
status = models.CharField(_("status"),max_length=const.DB_CHAR_CODE_2,default='0',choices=STATUS)
execute_time = models.DateTimeField(_("execute time"),blank=True,null=True)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
super(WareReturn,self).save(force_insert,force_update,using,update_fields)
item_count = ReturnItem.objects.filter(master=self).count()
if self.out and item_count == 0:
for out_item in OutItem.objects.filter(master=self.out):
ReturnItem.objects.create(master=self,out_item=out_item,material=out_item.material,price=out_item.price,
measure=out_item.measure,warehouse=out_item.warehouse,out_cnt=out_item.cnt,cnt=out_item.cnt)
def action_return(self,request):
with transaction.atomic():
total_amount = decimal.Decimal(0)
for item in ReturnItem.objects.filter(master=self):
if item.cnt > item.out_item.cnt or item.cnt < 0:
raise Exception('%s cnt is invalid,out is %s,return is %s' % (item.material,item.out_cnt,item.cnt))
item.event_time = datetime.datetime.now()
item.source = self.code
item.status = 1
item.out_item.inventory.cnt += item.cnt
item.out_item.inventory.save()
item.save()
total_amount += item.price * item.cnt
self.amount = total_amount
self.status = '9'
self.execute_time = datetime.datetime.now()
self.save()
class Meta:
verbose_name = _("ware return")
verbose_name_plural = _("ware return")
class WareAdjust(generic.BO):
"""
库存调整
"""
STATUS = (
('0', _("NEW")),
('1', _("IN PROGRESS")),
('9', _("EXECUTED"))
)
index_weight = 4
code = models.CharField(_("code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True)
title = models.CharField(_("title"),max_length=const.DB_CHAR_NAME_40)
description = models.TextField(_("description"),blank=True,null=True)
user = models.ForeignKey(User,verbose_name=_("out user"),blank=True,null=True)
status = models.CharField(_("status"),max_length=const.DB_CHAR_CODE_2,default='0',choices=STATUS)
execute_time = models.DateTimeField(_("execute time"),blank=True,null=True)
def action_adjust(self,request):
with transaction.atomic():
for item in AdjustItem.objects.filter(master=self,status=0):
inventory = item.inventory
if item.prop == '+':
inventory.cnt += item.cnt
else:
inventory.cnt -= item.cnt
inventory.save()
item.status = 1
item.event_time = datetime.datetime.now()
item.source = self.code
item.save()
self.status = '9'
self.execute_time = datetime.datetime.now()
self.save()
class Meta:
verbose_name = _("ware adjust")
verbose_name_plural = _("ware adjust")
class InOutDetail(models.Model):
"""
in out detail
"""
PROP = (
('+', _("PLUS")),
('-', _("MINUS"))
)
create_time = models.DateTimeField(_("create time"),auto_now_add=True)
status = models.BooleanField(_("executed"),default=0)
event_time = models.DateTimeField(_("event time"),blank=True,null=True)
warehouse = models.ForeignKey(Warehouse,verbose_name=_("warehouse"),blank=True,null=True)
material = models.ForeignKey(Material,verbose_name=_("material"),limit_choices_to={"is_virtual":"0"},blank=True,null=True)
measure = models.ForeignKey(Measure,verbose_name=_("measure"),blank=True,null=True)
cnt = models.DecimalField(_("count"),max_digits=14,decimal_places=4,blank=True,null=True)
batch = models.CharField(_("batch"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
price = models.DecimalField(_("price"),max_digits=14,decimal_places=4,blank=True,null=True)
prop = models.CharField(_("plus or minus property"),max_length=const.DB_CHAR_CODE_2,choices=PROP,default='+')
source = models.CharField(_("source"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
class InitItem(InOutDetail):
"""
期初入库明细
"""
master = models.ForeignKey(InitialInventory)
class Meta:
verbose_name = _("init item")
verbose_name_plural = _("init item")
class InItem(InOutDetail):
"""
入库单明细
"""
master = models.ForeignKey(StockIn)
po_item = models.ForeignKey(POItem,verbose_name=_("po item"),blank=True,null=True)
def get_new_price(self):
if self.po_item and self.master.warehouse:
try:
inventory = Inventory.objects.get(warehouse=self.master.warehouse,material=self.material,measure=self.measure)
if inventory and self.price != inventory.price:
total_amount = self.price*self.cnt+inventory.price*inventory.cnt
total_count = self.cnt+inventory.cnt
return total_amount/total_count
except Exception,e:
pass
return self.price or ''
class Meta:
verbose_name = _("in item")
verbose_name_plural = _("in item")
class InItemForm(forms.ModelForm):
new_price = forms.CharField(label=_('new price'),required=False)
class Meta:
model = InItem
fields = ('material', 'measure', 'cnt', 'price')
class OutItem(InOutDetail):
"""
出库单明细
"""
master = models.ForeignKey(StockOut)
inventory = models.ForeignKey(Inventory,blank=True,null=True,verbose_name=_("inventory material"))
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
self.prop = '-'
if self.inventory:
self.material = self.inventory.material
self.measure = self.inventory.measure
self.warehouse = self.inventory.warehouse
super(OutItem,self).save(force_insert,force_update,using,update_fields)
class Meta:
verbose_name = _("out item")
verbose_name_plural = _("out item")
class ReturnItem(InOutDetail):
"""
返库单明细
"""
master = models.ForeignKey(WareReturn)
out_item = models.ForeignKey(OutItem,blank=True,null=True,verbose_name=_('out item'))
out_cnt = models.DecimalField(_("out count"),max_digits=14,decimal_places=4)
class Meta:
verbose_name = _("return item")
verbose_name_plural = _("return item")
class AdjustItem(InOutDetail):
"""
库存调整明细
"""
master = models.ForeignKey(WareAdjust)
inventory = models.ForeignKey(Inventory,blank=True,null=True,verbose_name=_("inventory material"))
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
if self.inventory:
self.material = self.inventory.material
self.measure = self.inventory.measure
self.warehouse = self.inventory.warehouse
super(AdjustItem,self).save(force_insert,force_update,using,update_fields)
class Meta:
verbose_name = _("adjust item")
verbose_name_plural = _("adjust item")
+3
View File
@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.
+10
View File
@@ -0,0 +1,10 @@
from django.conf.urls import include, url,static
import invent.views
urlpatterns = [
url(r"stockin/(?P<object_id>\d+)/cin", invent.views.action_in),
url(r"initialinventory/(?P<object_id>\d+)/cin", invent.views.action_init),
url(r"stockout/(?P<object_id>\d+)/out", invent.views.action_out),
url(r"warereturn/(?P<object_id>\d+)/cin", invent.views.action_return),
url(r"wareadjust/(?P<object_id>\d+)/adjust", invent.views.action_adjust),
]
+175
View File
@@ -0,0 +1,175 @@
# coding=utf-8
from django.contrib.admin import site
from django.contrib.contenttypes.models import ContentType
from django.db import connection
from django.http.response import HttpResponseRedirect
from django.utils.encoding import force_text
from django.template.response import TemplateResponse
from django.contrib import messages
from django.contrib.auth.models import User
from invent.models import StockIn,StockOut,InitialInventory,WareReturn,WareAdjust
from django.utils.translation import ugettext_lazy as _
def action_in(request,object_id):
"""
入库操作
:param request:
:param object_id:
:return:
"""
title = _("Are you sure?")
obj = StockIn.objects.get(id=int(object_id))
opts = obj._meta
objects_name = force_text(opts.verbose_name)
if request.POST.get("post"):
try:
obj.action_entry(request)
messages.success(request,_('check in successfully'))
except Exception,e:
messages.error(request,e)
return HttpResponseRedirect("/admin/invent/stockin/%s"%(object_id))
context = dict(
site.each_context(request),
title=title,
opts=opts,
objects_name=objects_name,
object=obj,
)
request.current_app = site.name
return TemplateResponse(request,'admin/invent/stockin/in_confirmation.html', context)
def action_out(request,object_id):
"""
出库操作
:param request:
:param object_id:
:return:
"""
title = _("Are you sure?")
obj = StockOut.objects.get(id=int(object_id))
opts = obj._meta
objects_name = force_text(opts.verbose_name)
if request.POST.get("post"):
try:
obj.action_out(request)
messages.success(request,_('check out successfully'))
except Exception,e:
messages.error(request,e)
return HttpResponseRedirect("/admin/invent/stockout/%s"%(object_id))
context = dict(
site.each_context(request),
title=title,
opts=opts,
objects_name=objects_name,
object=obj,
)
request.current_app = site.name
return TemplateResponse(request,'admin/invent/stockout/out_confirmation.html', context)
def action_init(request,object_id):
"""
期初入库操作
:param request:
:param object_id:
:return:
"""
title = _("Are you sure?")
obj = InitialInventory.objects.get(id=int(object_id))
opts = obj._meta
objects_name = force_text(opts.verbose_name)
if request.POST.get("post"):
try:
obj.init_entry(request)
messages.success(request,_('check in successfully'))
except Exception,e:
messages.error(request,e)
return HttpResponseRedirect("/admin/invent/initialinventory/%s"%(object_id))
context = dict(
site.each_context(request),
title=title,
opts=opts,
objects_name=objects_name,
object=obj,
)
request.current_app = site.name
return TemplateResponse(request,'admin/invent/stockin/in_confirmation.html', context)
def action_return(request,object_id):
"""
返库操作
:param request:
:param object_id:
:return:
"""
title = _("Are you sure?")
obj = WareReturn.objects.get(id=int(object_id))
opts = obj._meta
objects_name = force_text(opts.verbose_name)
if request.POST.get("post"):
try:
obj.action_return(request)
messages.success(request,_('check in successfully'))
except Exception,e:
messages.error(request,e)
return HttpResponseRedirect("/admin/invent/warereturn/%s"%(object_id))
context = dict(
site.each_context(request),
title=title,
opts=opts,
objects_name=objects_name,
object=obj,
)
request.current_app = site.name
return TemplateResponse(request,'admin/invent/stockin/in_confirmation.html', context)
def action_adjust(request,object_id):
"""
调整操作
:param request:
:param object_id:
:return:
"""
title = _("Are you sure?")
obj = WareAdjust.objects.get(id=int(object_id))
opts = obj._meta
objects_name = force_text(opts.verbose_name)
if request.POST.get("post"):
try:
obj.action_adjust(request)
messages.success(request,_('check in successfully'))
except Exception,e:
messages.error(request,e)
return HttpResponseRedirect("/admin/invent/wareadjust/%s"%(object_id))
context = dict(
site.each_context(request),
title=title,
opts=opts,
objects_name=objects_name,
object=obj,
)
request.current_app = site.name
return TemplateResponse(request,'admin/invent/stockin/in_confirmation.html', context)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mis.settings")
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
+1
View File
@@ -0,0 +1 @@
__author__ = 'Administrator'
+176
View File
@@ -0,0 +1,176 @@
__author__ = 'zhugl'
# created at 15-4-21
#python import
from threading import local
from django.contrib import admin
from django.apps import apps
from django.conf import settings
from django.contrib.admin import ModelAdmin, actions
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.core.urlresolvers import NoReverseMatch, reverse
from django.db.models.base import ModelBase
from django.http import Http404, HttpResponseRedirect
from django.template.engine import Engine
from django.template.response import TemplateResponse
from django.utils import six
from django.utils.text import capfirst
from django.utils.translation import ugettext as _, ugettext_lazy
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
_thread_local = local()
def getuser():
return getattr(_thread_local,'user',None)
class RequestUser(object):
def process_request(self,request):
django_user = getattr(request,'user',None)
if django_user is not None:
_thread_local.user = django_user
def process_view(self, request, view_func, view_args, view_kwargs):
app_weight = {'selfhelp':1,'purchase':3,'sale':2,'invent':4,'organ':5,'basedata':6,'syscfg':7,'workflow':8}
if view_func.__name__ == 'index':
app_dict = {}
for model, model_admin in admin.site._registry.items():
app_label = model._meta.app_label
has_module_perms = model_admin.has_module_permission(request)
if has_module_perms:
perms = model_admin.get_model_perms(request)
if True in perms.values():
info = (app_label, model._meta.model_name)
model_dict = {
'name': capfirst(model._meta.verbose_name_plural),
'object_name': model._meta.object_name,
'perms': perms,
'weight':getattr(model,'index_weight',99)
}
if perms.get('change', False):
try:
model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=admin.site.name)
except NoReverseMatch:
pass
if perms.get('add', False):
try:
model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=admin.site.name)
except NoReverseMatch:
pass
if app_label in app_dict:
app_dict[app_label]['models'].append(model_dict)
else:
app_dict[app_label] = {
'name': apps.get_app_config(app_label).verbose_name,
'app_label': app_label,
'app_url': reverse(
'admin:app_list',
kwargs={'app_label': app_label},
current_app=admin.site.name,
),
'has_module_perms': has_module_perms,
'models': [model_dict],
'weight':app_weight.get(app_label,99)
}
app_list = list(six.itervalues(app_dict))
app_list.sort(key=lambda x: x['weight'])
for app in app_list:
app['models'].sort(key=lambda x: x['weight'])
context = dict(
maxi_app_list=app_list,
)
try:
todolist = self.get_my_task(request)
context.update(dict(todolist = todolist))
except Exception,e:
pass
# print context
view_kwargs['extra_context'] = context
if view_func.__name__ == 'app_index':
app_label = view_kwargs['app_label']
app_name = apps.get_app_config(app_label).verbose_name
app_dict = {}
lib_dict = {}
for model, model_admin in admin.site._registry.items():
if model_admin.has_module_permission(request):
label = model._meta.app_label
is_current = False
if label == app_label:
is_current = True
lib_dict[label] = {
'name': apps.get_app_config(label).verbose_name,
'app_label': label,
'app_url': reverse(
'admin:app_list',
kwargs={'app_label': label},
current_app=admin.site.name,
),
'weight':app_weight.get(label,99),
'is_current':is_current,
}
if app_label == model._meta.app_label:
has_module_perms = model_admin.has_module_permission(request)
if not has_module_perms:
raise PermissionDenied
perms = model_admin.get_model_perms(request)
if True in perms.values():
info = (app_label, model._meta.model_name)
model_dict = {
'name': capfirst(model._meta.verbose_name_plural),
'object_name': model._meta.object_name,
'perms': perms,
'weight':getattr(model,'index_weight',99)
}
if perms.get('change'):
try:
model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=admin.site.name)
except NoReverseMatch:
pass
if perms.get('add'):
try:
model_dict['add_url'] = reverse('admin:%s_%s_add' % info, current_app=admin.site.name)
except NoReverseMatch:
pass
if app_dict:
app_dict['models'].append(model_dict),
else:
app_dict = {
'name': app_name,
'app_label': app_label,
'app_url': '',
'has_module_perms': has_module_perms,
'models': [model_dict],
}
if not app_dict:
raise Http404('The requested admin page does not exist.')
# Sort the models alphabetically within each app.
app_dict['models'].sort(key=lambda x: x['weight'])
app_lib = list(six.itervalues(lib_dict))
app_lib.sort(key=lambda x: x['weight'])
context = dict(
maxi_app_list=[app_dict],
app_lib=app_lib,
)
view_kwargs['extra_context'] = context
def get_my_task(self,request):
from workflow.models import TodoList
if request and request.user:
query = TodoList.objects.filter(user=request.user,status=0)
if query.count() == 0:
return None
else:
return query.all()[:10]
+9
View File
@@ -0,0 +1,9 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import pymysql
pymysql.install_as_MySQLdb()
#解决mysql-python的历史遗留问题(Yuri_2017-04-29)
from django.contrib import admin
admin.site.site_header = '智捷ERP'
admin.site.site_title = '智捷ERP'
+127
View File
@@ -0,0 +1,127 @@
"""
Django settings for mis project.
Generated by 'django-admin startproject' using Django 1.8.
For more information on this file, see
https://docs.djangoproject.com/en/1.8/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.8/ref/settings/
"""
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '_5%1a5zxdjsb-je@85!l34g--ve7!skhc%^c2n)3vqyhq)yq@c'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
ALLOWED_HOSTS = ['127.0.0.1', 'localhost']
# Application definition
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'syscfg',
'basedata',
'organ',
'workflow',
'selfhelp',
'hr',
'invent',
'purchase',
'sale',
)
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'midware.cuser.RequestUser',
)
ROOT_URLCONF = 'mis.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
TEMPLATE_THEME = 'default'
WSGI_APPLICATION = 'mis.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST': '172.31.16.165',
'NAME': 'erp',
'USER': 'maximus',
'PASSWORD': 'maximus1234',
}
}
# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/
LANGUAGE_CODE = 'zh-CN'
LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale')]
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = False
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR,'static')
STATICFILES_DIRS = (
('css',os.path.join(STATIC_ROOT,'css')),
('js',os.path.join(STATIC_ROOT,'js')),
('img',os.path.join(STATIC_ROOT,'img')),
)
MEDIA_ROOT = os.path.join(BASE_DIR,'upload')
MEDIA_URL = '/upload/'
+129
View File
@@ -0,0 +1,129 @@
"""
Django settings for mis project.
Generated by 'django-admin startproject' using Django 1.8.
For more information on this file, see
https://docs.djangoproject.com/en/1.8/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.8/ref/settings/
"""
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '_5%1a5zxdjsb-je@85!l34g--ve7!skhc%^c2n)3vqyhq)yq@c'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['127.0.0.1', 'localhost']
# Application definition
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'syscfg',
'basedata',
'organ',
'workflow',
'selfhelp',
'hr',
'invent',
'purchase',
'sale',
)
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'midware.cuser.RequestUser',
)
ROOT_URLCONF = 'mis.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
TEMPLATE_THEME = 'default'
WSGI_APPLICATION = 'mis.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST': os.environ.get('DATABASE_HOST', 'localhost'),
'PORT': os.environ.get('DATABASE_PORT', '3306'),
'NAME': os.environ.get('DATABASE_NAME', 'mis'),
'USER': os.environ.get('DATABASE_USER', 'root'),
'PASSWORD': os.environ.get('DATABASE_PASSWORD', 'root'),
}
}
# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/
LANGUAGE_CODE = 'zh-hans'
LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale')]
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR,'static')
STATICFILES_DIRS = (
('css',os.path.join(STATIC_ROOT,'css')),
('js',os.path.join(STATIC_ROOT,'js')),
('img',os.path.join(STATIC_ROOT,'img')),
)
MEDIA_ROOT = os.path.join(BASE_DIR,'upload')
MEDIA_URL = '/upload/'
+21
View File
@@ -0,0 +1,21 @@
from django.conf.urls import include, url, static
from django.contrib import admin
from mis import settings
import workflow.views
import invent.urls
import basedata.urls
import selfhelp.urls
import mis.views
urlpatterns = [
url(r'^$', mis.views.home),
url(r"^admin/(?P<app>\w+)/(?P<model>\w+)/(?P<object_id>\d+)/start", workflow.views.start),
url(r"^admin/(?P<app>\w+)/(?P<model>\w+)/(?P<object_id>\d+)/approve/(?P<operation>\d+)", workflow.views.approve),
url(r"^admin/(?P<app>\w+)/(?P<model>\w+)/(?P<object_id>\d+)/restart/(?P<instance>\d+)", workflow.views.restart),
url(r'^admin/', include(admin.site.urls)),
url(r'^admin/invent/', include(invent.urls)),
url(r'^admin/basedata/', include(basedata.urls)),
url(r'^admin/selfhelp/', include(selfhelp.urls)),
]
urlpatterns += static.static(settings.STATIC_URL,document_root=settings.STATIC_ROOT)
urlpatterns += static.static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
+5
View File
@@ -0,0 +1,5 @@
from django.http.response import HttpResponseRedirect
def home(request):
return HttpResponseRedirect("/admin")
+16
View File
@@ -0,0 +1,16 @@
"""
WSGI config for mis project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mis.settings")
application = get_wsgi_application()
+1
View File
@@ -0,0 +1 @@
default_app_config = 'organ.apps.OrganConfig'
+68
View File
@@ -0,0 +1,68 @@
# coding=utf-8
from django.contrib import admin
from common import generic
from organ.models import Organization,OrgUnit,Position
from basedata.models import BankAccount
from basedata.admin import BankAccountInline
import datetime
class OrgAdmin(generic.BOAdmin):
CODE_PREFIX = 'O'
CODE_NUMBER_WIDTH = 2
list_display = ['code','name','represent','lic_code','cer_code']
fields = (
('name','code',),('short','pinyin',),
('tax_num','tax_account',),('tax_address',),('represent','email',),
('address','zipcode',),('contacts','phone',),('fax','website',),
('lic_code','cer_code',),('license','certificate',),('status','weight',),
)
inlines = [BankAccountInline]
class OrgUnitAdmin(generic.BOAdmin):
CODE_PREFIX = 'S'
CODE_NUMBER_WIDTH = 3
list_display = ['code','name','unit_type','parent']
list_display_links = ['code','name']
fields = (
('organization',),('parent',),('name','code',),('short','pinyin',),
('unit_type',),('status','virtual',),('phone','fax',),
('contacts','email',),('weight',),
)
def get_queryset(self, request):
return super(OrgUnitAdmin,self).get_queryset(request).filter(end__gt=datetime.date.today())
def save_model(self, request, obj, form, change):
if obj.parent and obj.parent.organization:
obj.organization = obj.parent.organization
obj.save()
super(OrgUnitAdmin,self).save_model(request,obj,form,change)
class PositionAdmin(generic.BOAdmin):
CODE_PREFIX = 'P'
CODE_NUMBER_WIDTH = 4
list_display = ['code','name','unit','series','grade','parent']
list_display_links = ['code','name']
fields = (
('unit',),('organization',),('parent',),('name','code',),('short','pinyin',),('series','grade',),
('virtual','status'),('description',),('qualification',),('document',),('weight',),
)
readonly_fields = ['organization']
def get_queryset(self, request):
return super(PositionAdmin,self).get_queryset(request).filter(end__gt=datetime.date.today())
def save_model(self, request, obj, form, change):
if obj.unit:
obj.organization = obj.unit.organization
obj.save()
super(PositionAdmin,self).save_model(request,obj,form,change)
admin.site.register(Position,PositionAdmin)
admin.site.register(OrgUnit,OrgUnitAdmin)
admin.site.register(Organization,OrgAdmin)
+9
View File
@@ -0,0 +1,9 @@
__author__ = 'zhugl'
# created at 15-4-22
from django.apps.config import AppConfig
from django.utils.translation import ugettext_lazy as _
class OrganConfig(AppConfig):
name = 'organ'
verbose_name = _('organization')
+115
View File
@@ -0,0 +1,115 @@
# coding=utf-8
from django.db import models
from django.db import connection
from django.utils.translation import ugettext_lazy as _
from common import const
from common import generic
class Organization(generic.BO):
"""
组织单位
"""
index_weight = 1
code = models.CharField(_("organ code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
name = models.CharField(_("organ name"),max_length=const.DB_CHAR_NAME_120)
short = models.CharField(_("short name"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
pinyin = models.CharField(_("pinyin"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
status = models.BooleanField(_("in use"),default=True)
tax_num = models.CharField(_("tax num"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
tax_address = models.CharField(_("tax address"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
tax_account = models.CharField(_("tax account"),max_length=const.DB_CHAR_NAME_80,blank=True,null=True)
represent = models.CharField(_("representative "),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
address = models.CharField(_("address"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
zipcode = models.CharField(_("zipcode"),max_length=const.DB_CHAR_CODE_8,blank=True,null=True)
fax = models.CharField(_("fax"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
contacts = models.CharField(_("contacts"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
phone = models.CharField(_("phone"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
website = models.CharField(_("website"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
email = models.CharField(_("email"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
lic_code = models.CharField(_("license code"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
cer_code = models.CharField(_("certificate code"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
license = models.FileField(_("business license"),blank=True,null=True,upload_to='organ')
certificate = models.FileField(_("organization code certificate"),blank=True,null=True,upload_to='organ')
weight = models.IntegerField(_("weight"),default=9)
class Meta:
verbose_name = _('organization')
verbose_name_plural = _('organization')
class OrgUnit(generic.BO):
"""
组织单元
"""
UNIT_LEVEL = (
(1,_('BRANCH')),
(2,_('DEPARTMENT')),
(3,_('OFFICE')),
(4,_('TEAM')),
(5,_('COMMITTEE'))
)
index_weight = 2
parent = models.ForeignKey('self',verbose_name=_("parent"),null=True,blank=True)
organization = models.ForeignKey(Organization,verbose_name = _('organization'),null=True,blank=True)
code = models.CharField(_("code"),max_length=const.DB_CHAR_CODE_8,blank=True,null=True)
name = models.CharField(_("name"),max_length=const.DB_CHAR_NAME_120)
short = models.CharField(_("short name"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
pinyin = models.CharField(_("pinyin"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
unit_type = models.IntegerField(_("type"),choices=UNIT_LEVEL,default=2)
status = models.BooleanField(_("in use"),default=True)
virtual = models.BooleanField(_("is virtual"),default=False)
fax = models.CharField(_("fax"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
phone = models.CharField(_("phone"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
contacts = models.CharField(_("contacts"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
email = models.CharField(_("email"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
weight = models.IntegerField(_("weight"),default=99)
class Meta:
verbose_name = _('org unit')
verbose_name_plural = _('org unit')
class Position(generic.BO):
"""
岗位
"""
SERIES = (
('A',_("Admin Position")),
('S',_("Sale Position")),
('T',_("Technology Position")),
('P',_("Produce Position")),
)
GRADE = (
('01', _("BASIC")),
('02', _("MEDIUM")),
('03', _("SENIOR")),
('04', _("PROFESSOR")),
('05', _("EXPERT")),
)
index_weight = 3
unit = models.ForeignKey(OrgUnit,verbose_name=_('org unit'))
organization = models.ForeignKey(Organization,verbose_name=_('organization'),null=True,blank=True)
parent = models.ForeignKey('self',verbose_name=_("parent"),null=True,blank=True)
code = models.CharField(_("position code"),max_length=const.DB_CHAR_CODE_8,blank=True,null=True)
name = models.CharField(_("position name"),max_length=const.DB_CHAR_NAME_120)
short = models.CharField(_("short name"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
pinyin = models.CharField(_("pinyin"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
series = models.CharField(_("position series"),max_length=1,default='A',choices=const.get_value_list('S014'))
grade = models.CharField(_("position grade"),max_length=const.DB_CHAR_CODE_2,default='01',choices=const.get_value_list('S015'))
virtual = models.BooleanField(_("is virtual"),default=False)
status = models.BooleanField(_("in use"),default=True)
description = models.TextField(_("position description"),blank=True,null=True)
qualification = models.TextField(_("qualification"),blank=True,null=True)
document = models.FileField(_("reference"),blank=True,null=True)
weight = models.IntegerField(_("weight"),default=99)
def __unicode__(self):
return u'%s %s' % (self.code,self.name)
class Meta:
verbose_name = _('position')
verbose_name_plural = _('position')
+3
View File
@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.
+3
View File
@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.
+3
View File
@@ -0,0 +1,3 @@
# created at 15-6-27
# coding=utf-8
__author__ = 'zhugl'
+54
View File
@@ -0,0 +1,54 @@
# created at 15-6-30
# coding=utf-8
__author__ = 'zhugl'
class Operation(object):
APPROVE = 1
DENY = 3
TERMINATE = 4
class WorkflowAction(object):
name = ''
description = ''
def action(self,request,obj,node_config,operation=Operation.APPROVE):
"""
:param request:
:param obj:
:param node_config:
:return:
"""
class TestAction(WorkflowAction):
name = 'action.test'
def action(self,request,obj,node_config,operation=Operation.APPROVE):
print 'this is a workflow test action'
print 'request user is %s,current node is %s'%(request.user,node_config)
class WorkflowActionManager(object):
"""
"""
actions = {}
registed = False
def __init__(self):
if WorkflowActionManager.registed:
pass
else:
WorkflowActionManager.register(TestAction)
WorkflowActionManager.registed = True
@classmethod
def register(cls,action):
if cls.actions.get(action.name):
raise Exception('%s already exists,register failed'%action.name)
if issubclass(action,WorkflowAction):
WorkflowActionManager.actions[action.name] = action()
+54
View File
@@ -0,0 +1,54 @@
# created at 15-6-30
# coding=utf-8
__author__ = 'zhugl'
from workflow.models import Node
class NextNodeHandler(object):
"""
"""
name = ''
description = ''
def handle(self,request,obj,node_config):
"""
:param request:
:param obj:
:param node_config:
:return:workflow.models.Node
"""
return None
class TestHandler(NextNodeHandler):
name = 'project.budge.gt.10000'
description = '预算金额大于10000,由总经理审批'
def handle(self,request,obj,node_config):
budget = getattr(obj,'budget',None)
if budget and budget > 10000:
return Node.objects.filter(id=7).all()
class NextNodeManager(object):
"""
"""
handlers = {}
registed = False
def __init__(self):
if NextNodeManager.registed:
pass
else:
NextNodeManager.register(TestHandler)
NextNodeManager.registed = True
@classmethod
def register(cls,handler):
if cls.handlers.get(handler.name):
raise Exception('%s already exists,register failed'%handler.name)
if issubclass(handler,NextNodeHandler):
NextNodeManager.handlers[handler.name] = handler()
+67
View File
@@ -0,0 +1,67 @@
# created at 15-6-30
# coding=utf-8
__author__ = 'zhugl'
class NextUserHandler(object):
"""
"""
name = ''
description = ''
def handle(self,request,obj,node_config):
"""
:param request:
:param obj:
:param node_config:
:return:django.contrib.auth.models.User[]
"""
return None
class UpPosition(NextUserHandler):
"""
"""
name = 'up.position.user'
def handle(self,request,obj,node_config):
from basedata.models import Employee,Position
emp_query = Employee.objects.filter(user=request.user)
if emp_query.count()>0:
emp = emp_query.all()
parent = []
for e in emp:
if e.position and e.position.parent:
parent.append(e.position.parent)
# print emp
# print parent
query2 = Employee.objects.filter(position__in=parent).exclude(user=None)
return [x.user for x in query2.all()]
else:
return None
class NextUserManager(object):
"""
"""
handlers = {}
registed = False
def __init__(self):
if NextUserManager.registed:
pass
else:
NextUserManager.register(UpPosition)
NextUserManager.registed = True
@classmethod
def register(cls,handler):
if cls.handlers.get(handler.name):
raise Exception('%s already exists,register failed'%handler.name)
if issubclass(handler,NextUserHandler):
NextUserManager.handlers[handler.name] = handler()
+183
View File
@@ -0,0 +1,183 @@
# created at 15-6-27
# coding=utf-8
__author__ = 'zhugl'
import xlrd
import os
import datetime
from mis import settings
class Handler(object):
name = ''
def handle(self,obj,f):
pass
class OPSHandler(Handler):
"""
导入基础信息部门岗位职员
"""
name = 'OPS'
def handle(self,obj,f):
if f and f.name.endswith('.xls'):
path = os.path.join(settings.MEDIA_ROOT,f.name)
workbook = xlrd.open_workbook(path)
for sheet in workbook.sheets():
if sheet.name == u'部门' or sheet.name == 'department':
self.department(obj,sheet)
elif sheet.name == u'岗位' or sheet.name == 'position':
self.position(obj,sheet)
elif sheet.name == u'职员' or sheet.name == 'employee':
self.stuff(obj,sheet)
def department(self,obj,sheet):
from organ.models import OrgUnit
row_count = sheet.nrows
if obj.is_clear:
OrgUnit.objects.update(end=datetime.date.today())
for row_index in range(1,row_count):
row = sheet.row_values(row_index)
weight = 99
if row[6]:
weight = row[6]
OrgUnit.objects.create(code=row[0],name=row[1],short=row[2],pinyin=row[3],begin=datetime.date.today(),
end=datetime.date(9999,12,31),weight=weight)
for row_index in range(1,row_count):
row = sheet.row_values(row_index)
if len(row[4]) > 0:
try:
parent = OrgUnit.objects.get(code=row[4])
OrgUnit.objects.filter(code=row[0]).update(parent=parent)
except Exception,e:
pass
def position(self,obj,sheet):
from organ.models import Position,OrgUnit
row_count = sheet.nrows
if obj.is_clear:
Position.objects.update(end=datetime.date.today())
for row_index in range(1,row_count):
row = sheet.row_values(row_index)
depart = None
if len(row[4]) > 0:
try:
depart = OrgUnit.objects.filter(code=row[4],end__gt=datetime.date.today()).all()[0]
except Exception,e:
pass
weight = 99
if row[6]:
weight = row[6]
Position.objects.create(code=row[0],name=row[1],unit=depart,begin=datetime.date.today(),
end=datetime.date(9999,12,31),weight=weight)
for row_index in range(1,row_count):
row = sheet.row_values(row_index)
if len(row[2]) > 0:
try:
parent = Position.objects.filter(code=row[2],end__gt=datetime.date.today()).all()[0]
Position.objects.filter(code=row[0]).update(parent=parent)
except Exception,e:
pass
def stuff(self,obj,sheet):
from organ.models import Position
from basedata.models import Employee
from django.contrib.auth.models import User,Group
row_count = sheet.nrows
try:
group = Group.objects.get_by_natural_key(u'职员')
except Exception,e:
pass
for row_index in range(1,row_count):
row = sheet.row_values(row_index)
position = Position.objects.filter(code=row[8],end__gt=datetime.date.today()).all()[0]
username = row[10]
email = row[11]
password = row[4][-6:]
if position is None:
raise Exception(u'职员%s-%s未分配岗位,或者您选择的岗位已失效,不可被引用'%(row[0],row[1]))
try:
employee = Employee.objects.get(code=row[0])
if employee.position.code == row[8]:
continue
else:
employee.position = position
employee.save()
except Exception,e:
employee = Employee.objects.create(code=row[0],name=row[1],pinyin=row[2],gender=row[3],idcard=row[4],
birthday=row[5],workday=row[6],startday=row[7],position=position)
if username:
try:
user = User.objects.get_by_natural_key(username)
except Exception,e:
user = User.objects.create_user(username=username,password=password)
user.is_staff = True
user.is_active = True
user.first_name = row[1]
if email:
user.email = email
if group:
user.groups.add(group)
user.save()
employee.user = user
employee.save()
class UserHandler(Handler):
"""
基础数据导入用户
"""
name = 'admin.user'
def handle(self,obj,f):
from django.contrib.auth.models import User,Group
if f and f.name.endswith('.xls'):
path = os.path.join(settings.MEDIA_ROOT,f.name)
workbook = xlrd.open_workbook(path)
sheet = workbook.sheet_by_index(0)
row_count = sheet.nrows
try:
group = Group.objects.get_by_natural_key(u'职员')
except Exception,e:
pass
for row_index in range(2,row_count):
row = sheet.row_values(row_index)
username = row[0]
password = row[1]
last_name = row[2]
first_name = row[3]
email = row[4]
if username == '':
continue
try:
user = User.objects.get_by_natural_key(username)
except Exception,e:
user = User.objects.create_user(username=username,password=password,email=email)
user.is_staff = True
user.is_active = True
user.last_name = last_name
user.first_name = first_name
if group:
user.groups.add(group)
user.save()
class ExcelManager(object):
"""
"""
handlers = {}
def __init__(self):
ExcelManager.register(OPSHandler)
ExcelManager.register(UserHandler)
@classmethod
def register(cls,handler):
if cls.handlers.get(handler.name):
raise Exception('%s already exists,register failed'%handler.name)
if issubclass(handler,Handler):
ExcelManager.handlers[handler.name] = handler()
+1
View File
@@ -0,0 +1 @@
default_app_config = "purchase.apps.MyAppConfig"
+90
View File
@@ -0,0 +1,90 @@
from django.contrib import admin
from common import generic
from purchase.models import PurchaseOrder,POItem,Invoice,Payment
from basedata.models import Partner,BankAccount
class POItemInline(admin.TabularInline):
model = POItem
fields = ('material', 'measure','price', 'cnt', 'tax', 'discount_price')
raw_id_fields = ['material']
def get_extra(self, request, obj=None, **kwargs):
if obj:
return 0
else:
return 3
class PurchaseOrderAdmin(generic.BOAdmin):
"""
"""
CODE_PREFIX = 'CG'
CODE_NUMBER_WIDTH = 5
list_display = ['code','title','order_date','partner','amount','discount_amount','payed_amount','invoice_amount','status']
list_display_links = ['code','title']
raw_id_fields = ['partner']
fields = (
('code','partner'),('order_date','arrive_date'),
('title','status',),('description',),('amount','discount_amount'),('attach',),
)
readonly_fields = ['status','amount']
inlines = [POItemInline]
date_hierarchy = 'begin'
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
if object_id:
extra_context = extra_context or {}
obj = PurchaseOrder.objects.get(id=object_id)
if obj.status == '99':
extra_context.update(dict(readonly=True))
return super(PurchaseOrderAdmin,self).changeform_view(request,object_id,form_url,extra_context)
def get_changeform_initial_data(self, request):
import datetime
begin = datetime.date.today()
end = begin + datetime.timedelta(30)
return {'order_date':begin,'arrive_date':end}
class PurchaseItemAdmin(generic.BOAdmin):
list_display = ['po','vender','material','cnt','price','tax']
readonly_fields = ['po','material','cnt','price','tax']
def get_queryset(self, request):
return POItem.objects.filter(left_cnt__gt=0)
class InvoiceAdmin(generic.BOAdmin):
list_display = ['code','number','vo_date','po','partner','po_amount','vo_amount']
readonly_fields = ['partner','po_amount']
raw_id_fields = ['po']
search_fields = ['po__code','partner__name']
date_hierarchy = 'begin'
fields = (
('vo_date',),('po',),('po_amount','partner',),('code',),('number',),('vo_amount',),('file',)
)
class PaymentAdmin(generic.BOAdmin):
CODE_PREFIX = 'PY'
CODE_NUMBER_WIDTH = 4
list_display = ['code','py_date','po','partner','po_amount','py_amount']
readonly_fields = ['partner','po_amount']
raw_id_fields = ['po']
search_fields = ['po__code','partner__name']
fields = (
('py_date','code',),('po',),('partner','po_amount',),('py_amount',),('bank',),('response_code'),('memo',)
)
date_hierarchy = 'begin'
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
if db_field.name == 'bank':
kwargs['queryset'] = BankAccount.objects.exclude(org__exact=None)
return super(PaymentAdmin,self).formfield_for_foreignkey(db_field,request,**kwargs)
admin.site.register(PurchaseOrder,PurchaseOrderAdmin)
admin.site.register(POItem,PurchaseItemAdmin)
admin.site.register(Invoice,InvoiceAdmin)
admin.site.register(Payment,PaymentAdmin)
+11
View File
@@ -0,0 +1,11 @@
# created at 15-5-23
# coding=utf-8
__author__ = 'zhugl'
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class MyAppConfig(AppConfig):
name = 'purchase'
verbose_name = _("purchase manage")
+221
View File
@@ -0,0 +1,221 @@
# coding=utf-8
import datetime
import os
import xlrd
import decimal
from django.db import transaction
from django.db import models
from django.db.models.aggregates import Sum
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from django.utils.text import force_text
from mis import settings
from common import generic
from common import const
from selfhelp.models import WOItem
from basedata.models import Material,Organization,Partner,Measure,BankAccount
class PurchaseOrder(generic.BO):
"""
"""
STATUS = (
('0', _("NEW")),
('1', _("IN PROGRESS")),
('4', _("DROP")),
('9', _("APPROVED")),
('99', _("ALREADY STOCK IN")),
)
index_weight = 1
code = models.CharField(_("code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
partner = models.ForeignKey(Partner,verbose_name=_("partner"),limit_choices_to={"partner_type":"S"})
order_date = models.DateField(_("order date"))
arrive_date = models.DateField(_("arrive date"))
org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True)
title = models.CharField(_("title"),max_length=const.DB_CHAR_NAME_40)
description = models.TextField(_("description"),blank=True,null=True)
user = models.ForeignKey(User,verbose_name=_("user"),blank=True,null=True)
status = models.CharField(_("status"),max_length=const.DB_CHAR_CODE_2,default='0',choices=STATUS)
amount = models.DecimalField(_("money amount"),max_digits=12,decimal_places=2,blank=True,null=True,default=0.00)
discount_amount = models.DecimalField(_("discount amount"),max_digits=12,decimal_places=2,blank=True,null=True,default=0.00)
entry_status = models.BooleanField(_("entry status"),default=0)
entry_time = models.DateTimeField(_("entry time"),blank=True,null=True)
attach = models.FileField(_('attach'),blank=True,null=True,help_text=u'您可导入采购明细,模板请参考文档FD0008')
def __unicode__(self):
return u'%s %s' % (self.code,self.title)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
super(PurchaseOrder,self).save(force_insert,force_update,using,update_fields)
if self.discount_amount > 0:
sql = 'UPDATE purchase_poitem a SET a.discount_price = ' \
'a.price-((SELECT discount_amount/amount FROM purchase_purchaseorder WHERE id=%s)*a.amount/a.cnt) WHERE a.po_id = %s'
params = [self.id,self.id]
generic.update(sql,params)
item_count = POItem.objects.filter(po=self).count()
if self.attach and item_count == 0:
path = os.path.join(settings.MEDIA_ROOT,self.attach.name)
workbook = xlrd.open_workbook(path)
sheet = workbook.sheet_by_index(0)
row_count = sheet.nrows
with transaction.atomic():
total_amount = decimal.Decimal(0)
for row_index in range(row_count):
row = sheet.row_values(row_index)
if row_index == 0:
doc_type = row[1]
if doc_type.startswith('0'):
break
else:
continue
elif row_index < 3:
continue
material = None
measure = None
try:
measure = Measure.objects.get(code=row[4])
except Exception,e:
measure = Measure.objects.create(code=row[4],name=force_text(row[5]))
try:
material = Material.objects.get(code=row[0])
except Exception,e:
material = Material(code=row[0],name=force_text(row[1]),spec=force_text(row[2]))
material.purchase_price = row[6]
material.save()
amount = decimal.Decimal(row[6])*decimal.Decimal(row[7])
POItem.objects.create(po=self,material=material,measure=measure,cnt=row[7],price=row[6],amount=amount)
total_amount += amount
sql = 'update purchase_purchaseorder set amount = %s where id=%s'
params = [total_amount,self.id]
generic.update(sql,params)
def invoice_amount(self):
total = Invoice.objects.filter(po=self).aggregate(Sum('vo_amount')).get('vo_amount__sum') or 0.00
return total
def payed_amount(self):
return Payment.objects.filter(po=self).aggregate(Sum('py_amount')).get('py_amount__sum') or 0.00
invoice_amount.short_description = _('invoice amount')
payed_amount.short_description = _('pay amount')
class Meta:
verbose_name = _("purchase order")
verbose_name_plural = _("purchase orders")
class POItem(models.Model):
"""
"""
index_weight = 2
po = models.ForeignKey(PurchaseOrder,verbose_name=_("purchase order"))
material = models.ForeignKey(Material,verbose_name=_("material"),limit_choices_to={"is_virtual":"0"})
measure = models.ForeignKey(Measure,verbose_name=_("measure"),blank=True,null=True)
price = models.DecimalField(_("price"),max_digits=12,decimal_places=4,blank=True,null=True)
cnt = models.DecimalField(_("count"),max_digits=12,decimal_places=4,blank=True,null=True)
discount_price = models.DecimalField(_("discount price"),max_digits=12,decimal_places=4,blank=True,null=True)
amount = models.DecimalField(_("money of amount"),max_digits=12,decimal_places=2,blank=True,null=True)
discount_amount = models.DecimalField(_("discount amount"),max_digits=12,decimal_places=2,blank=True,null=True)
tax = models.CharField(_("tax rate"),max_length=const.DB_CHAR_CODE_6,choices=const.get_value_list('S052'),default='0.00')
woitem = models.ForeignKey(WOItem,verbose_name=_("wo item"),blank=True,null=True)
is_in_stock = models.BooleanField(_("is in stock"),default=0)
in_stock_time = models.DateTimeField(_("execute time"),blank=True,null=True)
entry_cnt = models.DecimalField(_("entry count"),max_digits=12,decimal_places=4,blank=True,null=True)
left_cnt = models.DecimalField(_("left count"),max_digits=12,decimal_places=4,blank=True,null=True)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
if self.price and self.cnt:
money = self.price * self.cnt
self.amount = money
if self.measure is None and self.material and self.material.measure.count() > 0:
self.measure = self.material.measure.all()[0]
if self.is_in_stock:
self.left_cnt -= self.entry_cnt
else:
self.left_cnt = self.cnt
super(POItem,self).save(force_insert,force_update,using,update_fields)
self.material.purchase_price = self.price
self.material.save()
sql = 'UPDATE purchase_purchaseorder SET amount = (SELECT SUM(a.price*a.cnt) AS amount FROM ' \
'purchase_poitem a WHERE a.po_id = %s) WHERE id = %s'
params = [self.po.id,self.po.id]
generic.update(sql,params)
def vender(self):
return u'%s' % (self.po.partner)
vender.short_description = _("partner")
class Meta:
verbose_name = _("po item")
verbose_name_plural = _("po item")
class Invoice(generic.BO):
"""
采购发票
"""
index_weight = 4
vo_date = models.DateField(_("invoice date"),blank=True,null=True,default=datetime.date.today)
code = models.CharField(_("invoice code"),max_length=const.DB_CHAR_NAME_20)
number = models.CharField(_("invoice number"),max_length=const.DB_CHAR_NAME_20)
po = models.ForeignKey(PurchaseOrder,verbose_name=_("purchase order"))
partner = models.ForeignKey(Partner,verbose_name=_("partner"),blank=True,null=True)
po_amount = models.DecimalField(_("po amount"),max_digits=14,decimal_places=4,blank=True,null=True)
vo_amount = models.DecimalField(_("invoice amount"),max_digits=14,decimal_places=4)
file = models.FileField(_("invoice file"),upload_to='invoice',blank=True,null=True)
def __unicode__(self):
return u"%s %s" % (self.code,self.partner)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
if self.po:
self.partner = self.po.partner
self.po_amount = self.po.amount
super(Invoice,self).save(force_insert,force_update,using,update_fields)
class Meta:
verbose_name = _("Invoice")
verbose_name_plural = _("Invoice")
class Payment(generic.BO):
"""
采购付款
"""
index_weight = 3
py_date = models.DateField(_("pay date"),blank=True,null=True,default=datetime.date.today)
org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True)
code = models.CharField(_("pay code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
po = models.ForeignKey(PurchaseOrder,verbose_name=_("purchase order"))
partner = models.ForeignKey(Partner,verbose_name=_("partner"),blank=True,null=True)
po_amount = models.DecimalField(_("po amount"),max_digits=14,decimal_places=4,blank=True,null=True)
py_amount = models.DecimalField(_("pay amount"),max_digits=14,decimal_places=4)
bank = models.ForeignKey(BankAccount,verbose_name=_("bank account"),blank=True,null=True)
response_code = models.CharField(_("response code"),max_length=const.DB_CHAR_NAME_80,blank=True,null=True)
memo = models.TextField(_("memo"),blank=True,null=True)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
if self.po:
self.partner = self.po.partner
self.po_amount = self.po.amount
super(Payment,self).save(force_insert,force_update,using,update_fields)
def __unicode__(self):
return u"%s %s" % (self.code,self.partner)
class Meta:
verbose_name = _("Payment")
verbose_name_plural = _("Payment")
+3
View File
@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.
+3
View File
@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.
+1
View File
@@ -0,0 +1 @@
default_app_config = "sale.apps.MyAppConfig"
+104
View File
@@ -0,0 +1,104 @@
from django.contrib import admin
from common import generic
from sale.models import SaleOrder,SaleItem,PaymentCollection,OfferSheet,OfferItem
from basedata.models import Measure,BankAccount
from common import generic
import datetime
class SaleItemInline(admin.TabularInline):
model = SaleItem
fields = ('material','measure','sale_price','discount_price','cnt','tax')
raw_id_fields = ['material']
def get_extra(self, request, obj=None, **kwargs):
if obj:
return 0
else:
return 3
class OfferItemInline(admin.TabularInline):
model = OfferItem
fields = ('material','brand','measure','cost_price','sale_price','discount_price','cnt','tax')
raw_id_fields = ['material']
readonly_fields = ['brand']
def get_extra(self, request, obj=None, **kwargs):
if obj:
return 0
else:
return 3
class SaleOrderAdmin(generic.BOAdmin):
CODE_NUMBER_WIDTH = 5
CODE_PREFIX = 'SO'
inlines = [SaleItemInline]
list_display = ['code','title','order_date','partner','amount','collection_amount']
list_display_links = ['code','title']
raw_id_fields = ['partner','user','org']
fields = (
('code','org',),('title','invoice_type',),('partner','user',),('order_date','deliver_date',),
('contact','phone',),('deliver_address','fax',),('description',),('amount','discount_amount','status')
)
readonly_fields = ['amount','status']
date_hierarchy = 'begin'
def save_model(self, request, obj, form, change):
if obj:
obj.user = request.user
super(SaleOrderAdmin,self).save_model(request,obj,form,change)
def get_changeform_initial_data(self, request):
today = datetime.datetime.today()
deadline = today+datetime.timedelta(days=30)
return {'order_date':today,'deliver_date':deadline}
class OfferSheetAdmin(generic.BOAdmin):
CODE_NUMBER_WIDTH = 5
CODE_PREFIX = 'BJ'
inlines = [OfferItemInline]
list_display = ['code','title','offer_date','partner','amount']
list_display_links = ['code','title']
raw_id_fields = ['partner','user','org']
fields = (
('code','org',),('partner','user',),('offer_date','deliver_date',),('title',),
('description',),('amount','discount_amount',),('attach'),
)
readonly_fields = ['amount']
date_hierarchy = 'begin'
def save_model(self, request, obj, form, change):
if obj:
obj.user = request.user
super(OfferSheetAdmin,self).save_model(request,obj,form,change)
def get_changeform_initial_data(self, request):
today = datetime.datetime.today()
deadline = today+datetime.timedelta(days=30)
return {'offer_date':today,'deliver_date':deadline}
class PaymentCollectionAdmin(generic.BOAdmin):
CODE_NUMBER_WIDTH = 4
CODE_PREFIX = 'CP'
list_display = ['code','so','partner','order_amount','collection_date','collection_amount']
fields = (
('code',),('collection_date',),('so',),('partner',),('order_amount','collection_amount',),('bank',),
('memo',)
)
raw_id_fields = ['so','partner']
readonly_fields = ['order_amount']
list_display_links = ['code','so']
date_hierarchy = 'begin'
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
if db_field.name=='bank':
kwargs['queryset'] = BankAccount.objects.exclude(org__exact=None)
return super(PaymentCollectionAdmin,self).formfield_for_foreignkey(db_field,request,**kwargs)
admin.site.register(SaleOrder,SaleOrderAdmin)
admin.site.register(PaymentCollection,PaymentCollectionAdmin)
admin.site.register(OfferSheet,OfferSheetAdmin)
+11
View File
@@ -0,0 +1,11 @@
# created at 15-5-23
# coding=utf-8
__author__ = 'zhugl'
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class MyAppConfig(AppConfig):
name = 'sale'
verbose_name = _("sale management")
+252
View File
@@ -0,0 +1,252 @@
# coding=utf-8
import decimal
import datetime
import csv
import os
import xlrd
from mis import settings
from django.db import models
from django.db import transaction
from django.db.models.aggregates import Sum
from django.contrib.auth.models import User
from django.utils.text import force_text
from django.utils.translation import ugettext_lazy as _
from common import generic
from common import const
from basedata.models import Material,Project,Partner,Organization,Measure,BankAccount
class SaleOrder(generic.BO):
"""
销售订单
"""
STATUS = (
('0', _("NEW")),
('1', _("IN PROGRESS")),
('4', _("DROP")),
('9', _("APPROVED")),
('99', _("ALREADY STOCK OUT")),
)
index_weight = 2
code = models.CharField(_("code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
partner = models.ForeignKey(Partner,verbose_name=_("partner"),limit_choices_to={"partner_type":"C"})
order_date = models.DateField(_("order date"))
deliver_date = models.DateField(_("deliver date"))
org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True)
title = models.CharField(_("title"),max_length=const.DB_CHAR_NAME_40)
description = models.TextField(_("memo"),blank=True,null=True)
contact = models.CharField(_("contacts"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
phone = models.CharField(_("phone"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
fax = models.CharField(_("fax"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
deliver_address = models.CharField(_("deliver address"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
invoice_type = models.CharField(_("invoice type"),max_length=const.DB_CHAR_CODE_6,choices=const.get_value_list('S053'),default='10')
amount = models.DecimalField(_("money amount"),max_digits=12,decimal_places=2,blank=True,null=True,default=0.00)
discount_amount = models.DecimalField(_("discount amount"),max_digits=12,decimal_places=2,blank=True,null=True,default=0.00)
user = models.ForeignKey(User,verbose_name=_("sales man"),blank=True,null=True)
status = models.CharField(_("status"),max_length=const.DB_CHAR_CODE_2,default='0',choices=STATUS)
def __unicode__(self):
return u'%s %s' % (self.code,self.title)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
super(SaleOrder,self).save(force_insert,force_update,using,update_fields)
if self.discount_amount > 0:
sql = 'UPDATE sale_saleitem a SET a.discount_price = a.sale_price - ' \
'((SELECT discount_amount/amount FROM sale_saleorder WHERE id = %s) * (a.sale_price*a.cnt)/a.cnt) WHERE a.master_id = %s'
params = [self.id,self.id]
generic.update(sql,params)
def collection_amount(self):
return PaymentCollection.objects.filter(so=self).aggregate(Sum('collection_amount')).get('collection_amount__sum') or 0.00
collection_amount.short_description = _('collection amount')
class Meta:
verbose_name = _('sale order')
verbose_name_plural = _('sale order')
class SaleItem(models.Model):
"""
订单明细
"""
master = models.ForeignKey(SaleOrder)
material = models.ForeignKey(Material,verbose_name=_("material"),limit_choices_to={"is_virtual":"0",'can_sale':'1'},blank=True,null=True)
measure = models.ForeignKey(Measure,verbose_name=_("measure"),blank=True,null=True)
cnt = models.DecimalField(_("count"),max_digits=14,decimal_places=4)
stock_price = models.DecimalField(_("stock price"),max_digits=14,decimal_places=4,blank=True,null=True)
sale_price = models.DecimalField(_("sale price"),max_digits=14,decimal_places=4,blank=True,null=True)
discount_price = models.DecimalField(_("discount price"),max_digits=14,decimal_places=4,blank=True,null=True)
tax = models.CharField(_("tax rate"),max_length=const.DB_CHAR_CODE_6,choices=const.get_value_list('S052'),default='0.00')
create_time = models.DateTimeField(_("create time"),auto_now_add=True)
status = models.BooleanField(_("executed"),default=0)
event_time = models.DateTimeField(_("event time"),blank=True,null=True)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
if self.material and self.material.measure.count() > 0:
self.measure = self.material.measure.all()[0]
if self.material.sale_price or self.material.stock_price:
self.sale_price = self.material.sale_price or self.material.stock_price*decimal.Decimal(1.17)/decimal.Decimal(0.6)
super(SaleItem,self).save(force_insert,force_update,using,update_fields)
sql = 'update sale_saleorder set amount = (select sum(sale_price*cnt) from sale_saleitem where master_id=%s) where id=%s'
params = [self.master.id,self.master.id]
# print sql % (self.master.id,self.master.id)
generic.update(sql,params)
class Meta:
verbose_name = _('order detail')
verbose_name_plural = _('order detail')
class PaymentCollection(generic.BO):
"""
销售回款
"""
index_weight = 3
collection_date = models.DateField(_("collection date"),blank=True,null=True,default=datetime.date.today)
org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True)
code = models.CharField(_("collection code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
so = models.ForeignKey(SaleOrder,verbose_name=_("sale order"))
partner = models.ForeignKey(Partner,verbose_name=_("partner"),blank=True,null=True)
order_amount = models.DecimalField(_("order amount"),max_digits=14,decimal_places=4,blank=True,null=True)
collection_amount = models.DecimalField(_("collection amount"),max_digits=14,decimal_places=4)
bank = models.ForeignKey(BankAccount,verbose_name=_("bank account"),blank=True,null=True)
memo = models.TextField(_("memo"),blank=True,null=True)
def __unicode__(self):
return u'%s %s'%(self.code,self.so)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
if self.so:
self.partner = self.so.partner
self.order_amount = self.so.amount
if self.so.discount_amount > 0:
self.order_amount -= self.so.discount_amount
super(PaymentCollection,self).save(force_insert,force_update,using,update_fields)
class Meta:
verbose_name = _('Payment Collection')
verbose_name_plural = _('Payment Collection')
class OfferSheet(generic.BO):
"""
报价单
"""
index_weight = 1
STATUS = (
('0', _("NEW")),
('1', _("IN PROGRESS")),
('4', _("DROP")),
('9', _("APPROVED")),
)
code = models.CharField(_("code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
partner = models.ForeignKey(Partner,verbose_name=_("partner"),limit_choices_to={"partner_type":"C"})
offer_date = models.DateField(_("offer date"))
deliver_date = models.DateField(_("deliver date"))
org = models.ForeignKey(Organization,verbose_name=_("organization"),blank=True,null=True)
title = models.CharField(_("title"),max_length=const.DB_CHAR_NAME_40)
description = models.TextField(_("memo"),blank=True,null=True)
amount = models.DecimalField(_("money amount"),max_digits=12,decimal_places=2,blank=True,null=True,default=0.00)
discount_amount = models.DecimalField(_("discount amount"),max_digits=12,decimal_places=2,blank=True,null=True,default=0.00)
user = models.ForeignKey(User,verbose_name=_("offer man"),blank=True,null=True)
attach = models.FileField(_('offer sheet file'),blank=True,null=True,upload_to='offer sheet',help_text=u'您可导入报价明细,模板请参考文档FD0006')
status = models.BooleanField(_("executed"),default=0)
event_time = models.DateTimeField(_("event time"),blank=True,null=True)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
import decimal
super(OfferSheet,self).save(force_insert,force_update,using,update_fields)
if self.discount_amount > 0:
sql = 'UPDATE sale_offeritem a SET a.discount_price = a.sale_price - ' \
'((SELECT discount_amount/amount FROM sale_offersheet WHERE id = %s) * (a.sale_price*a.cnt)/a.cnt) WHERE a.master_id = %s'
params = [self.id,self.id]
# print sql % (self.id,self.id)
generic.update(sql,params)
item_count = OfferItem.objects.filter(master=self).count()
if self.attach and item_count == 0:
path = os.path.join(settings.MEDIA_ROOT,self.attach.name)
workbook = xlrd.open_workbook(path)
sheet = workbook.sheet_by_index(0)
row_count = sheet.nrows
with transaction.atomic():
total_amount = decimal.Decimal(0)
for row_index in range(row_count):
row = sheet.row_values(row_index)
if row_index == 0:
doc_type = row[1]
if doc_type.startswith('0'):
break
else:
continue
elif row_index < 3:
continue
material = None
measure = None
try:
measure = Measure.objects.get(code=row[4])
except Exception,e:
measure = Measure.objects.create(code=row[4],name=force_text(row[5]))
try:
material = Material.objects.get(code=row[0])
except Exception,e:
material = Material(code=row[0],name=force_text(row[1]),spec=force_text(row[2]))
material.sale_price = row[6]
material.purchase_price = row[7]
material.save()
OfferItem.objects.create(master=self,material=material,measure=measure,cnt=row[8],brand=force_text(row[3]),
cost_price=row[7],sale_price=row[6])
total_amount += decimal.Decimal(row[6])*decimal.Decimal(row[8])
sql = 'update sale_offersheet set amount = %s where id=%s'
params = [total_amount,self.id]
generic.update(sql,params)
class Meta:
verbose_name = _('offer sheet')
verbose_name_plural = _('offer sheet')
class OfferItem(models.Model):
"""
订单明细
"""
master = models.ForeignKey(OfferSheet)
material = models.ForeignKey(Material,verbose_name=_("material"),limit_choices_to={"is_virtual":"0",'can_sale':'1'},blank=True,null=True)
measure = models.ForeignKey(Measure,verbose_name=_("measure"),blank=True,null=True)
cnt = models.DecimalField(_("count"),max_digits=14,decimal_places=4)
brand = models.CharField(_('brand'),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
cost_price = models.DecimalField(_("cost price"),max_digits=14,decimal_places=4,blank=True,null=True)
stock_price = models.DecimalField(_("stock price"),max_digits=14,decimal_places=4,blank=True,null=True)
sale_price = models.DecimalField(_("sale price"),max_digits=14,decimal_places=4,blank=True,null=True)
discount_price = models.DecimalField(_("discount price"),max_digits=14,decimal_places=4,blank=True,null=True)
tax = models.CharField(_("tax rate"),max_length=const.DB_CHAR_CODE_6,choices=const.get_value_list('S052'),default='0.00')
create_time = models.DateTimeField(_("create time"),auto_now_add=True)
status = models.BooleanField(_("executed"),default=0)
event_time = models.DateTimeField(_("event time"),blank=True,null=True)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
if self.material and self.material.measure.count() > 0:
self.measure = self.material.measure.all()[0]
if self.material.sale_price or self.material.stock_price:
self.sale_price = self.material.sale_price or self.material.stock_price*decimal.Decimal(1.17)/decimal.Decimal(0.6)
super(OfferItem,self).save(force_insert,force_update,using,update_fields)
sql = 'update sale_offersheet set amount = (select sum(sale_price*cnt) from sale_offeritem where master_id=%s) where id=%s'
params = [self.master.id,self.master.id]
# print sql % (self.master.id,self.master.id)
generic.update(sql,params)
class Meta:
verbose_name = _('offer detail')
verbose_name_plural = _('offer detail')
+3
View File
@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.
+3
View File
@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.
+1
View File
@@ -0,0 +1 @@
default_app_config = "selfhelp.apps.MyAppConfig"
+219
View File
@@ -0,0 +1,219 @@
# coding=utf-8
# coding = utf-8
import datetime
from django.contrib import admin
from django.contrib import messages
from django.http import HttpResponseRedirect
from django.db.models.aggregates import Sum
from django.utils.translation import ugettext_lazy as _
from common import generic
from common import const
from basedata.models import Material,ExtraParam,Employee,Position
from selfhelp.models import WorkOrder,WOExtraValue,WOItem,Reimbursement,ReimbursementItem,Loan,Enroll,Feedback,Activity
class ParamValueInline(admin.TabularInline):
model = WOExtraValue
fields = ('param_name','param_value')
readonly_fields = ['param_name']
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
if db_field.name == 'param_name':
app_info = generic.get_app_model_info_from_request(request)
instance = app_info['obj']
if instance:
kwargs['queryset'] = ExtraParam.objects.filter(material=instance.service)
return super(ParamValueInline,self).formfield_for_foreignkey(db_field,request,**kwargs)
def get_extra(self, request, obj=None, **kwargs):
if obj:
return 0
else:
return 1
class ItemInline(admin.TabularInline):
model = WOItem
raw_id_fields = ['material']
def get_extra(self, request, obj=None, **kwargs):
if obj:
return 0
else:
return 1
class WorkOrderAdmin(generic.BOAdmin):
"""
"""
CODE_PREFIX = 'WO'
CODE_NUMBER_WIDTH = 5
list_display = ['code','begin','title','classification','business_domain','status']
list_display_links = ['code','title']
exclude = ['creator','modifier','creation','modification']
search_fields = ['code','title']
list_filter = ['classification','service','status']
fields = (
('begin','end',),
('code','refer',),('classification','business_domain',),
('service','project',),
('title','status',),('description',),('attach',),('detail',)
)
readonly_fields = ['status']
raw_id_fields = ['service','project','refer']
inlines = [ItemInline,ParamValueInline]
date_hierarchy = 'begin'
def save_model(self, request, obj, form, change):
if obj.user is None:
obj.user = request.user
super(WorkOrderAdmin,self).save_model(request,obj,form,change)
def get_changeform_initial_data(self, request):
import datetime
td = datetime.date.today()
end = td + datetime.timedelta(30)
return {'begin':datetime.date.today, 'end':end}
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
if db_field.name == 'refer':
app_info = generic.get_app_model_info_from_request(request)
if app_info and app_info['obj']:
kwargs['queryset'] = WorkOrder.objects.exclude(id=app_info['id'])
return super(WorkOrderAdmin,self).formfield_for_foreignkey(db_field,request,**kwargs)
class LoanAdmin(generic.BOAdmin):
CODE_PREFIX = 'JK'
CODE_NUMBER_WIDTH = 5
list_display = ['code','title','project','loan_amount','applier','status']
list_display_links = ['code','title']
readonly_fields = ['status','logout_time','logout_amount']
raw_id_fields = ['project','user']
fields = (
('code',),('title','loan_amount',),('description',),('project'),('user','status'),('logout_time','logout_amount',),
)
extra_buttons = [{'href':'pay','title':_('pay')}]
search_fields = ['code','title','user__username']
date_hierarchy = 'begin'
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
if object_id:
try:
obj = Loan.objects.get(id=object_id)
if obj and obj.status == 'P':
extra_context = extra_context or {}
extra_context.update(dict(readonly=True))
except Exception,e:
pass
return super(LoanAdmin,self).changeform_view(request,object_id,form_url,extra_context)
def save_model(self, request, obj, form, change):
if obj and obj.user is None:
obj.user = request.user
super(LoanAdmin,self).save_model(request,obj,form,change)
class ReimbursementItemInline(admin.TabularInline):
model = ReimbursementItem
raw_id_fields = ['expense_account']
def get_extra(self, request, obj=None, **kwargs):
if obj:
return 0
else:
return 1
class ReimbursementAdmin(generic.BOAdmin):
CODE_PREFIX = 'BX'
CODE_NUMBER_WIDTH = 5
list_display = ['code','title','project','amount','applier','status']
list_display_links = ['code','title']
inlines = [ReimbursementItemInline]
raw_id_fields = ['project','wo','user','org']
readonly_fields = ['loan_amount','pay_time','status','amount']
fieldsets = [
(None,{'fields':[('code','user'),('title','amount','status',),('description',),('project','wo',)]}),
(_('fico'),{'fields':[('org',),('loan',),('logout_amount','pay_amount',)],'classes': ['collapse']})
]
extra_buttons = [{'href':'pay','title':_('pay')}]
search_fields = ['code','title','project__code','project__name','user__username']
date_hierarchy = 'begin'
def get_changeform_initial_data(self, request):
apps = generic.get_app_model_info_from_request(request)
obj = getattr(apps,'obj',None)
current = request.user
if obj:
current = obj.user
sm = Loan.objects.filter(user=current).aggregate(Sum('loan_amount')).get('loan_amount__sum') or 0.00
return {'loan_amount':sm}
def save_model(self, request, obj, form, change):
if obj and obj.user is None:
obj.user = request.user
super(ReimbursementAdmin,self).save_model(request,obj,form,change)
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
if db_field.name == 'loan':
apps = generic.get_app_model_info_from_request(request)
current = request.user
if apps:
obj = apps.get('obj')
current = obj.user
if obj.status == 'P':
kwargs['queryset']=Loan.objects.filter(id=obj.loan.id)
else:
kwargs['queryset']=Loan.objects.filter(user=current,is_clear=0)
else:
kwargs['queryset']=Loan.objects.filter(user=current,is_clear=0)
return super(ReimbursementAdmin,self).formfield_for_foreignkey(db_field,request,**kwargs)
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
if object_id:
try:
obj = Reimbursement.objects.get(id=object_id)
if obj and obj.status == 'P':
extra_context = extra_context or {}
extra_context.update(dict(readonly=True))
except Exception,e:
pass
return super(ReimbursementAdmin,self).changeform_view(request,object_id,form_url,extra_context)
class EnrollInline(admin.TabularInline):
model = Enroll
class FeedbackInline(admin.TabularInline):
model = Feedback
class ActivityAdmin(generic.BOAdmin):
CODE_PREFIX = 'AC'
CODE_NUMBER_WIDTH = 5
list_display = ['code','begin_time','end_time','title','classification','room']
list_display_links = ['code','title']
raw_id_fields = ['room','parent']
fieldsets = [
(None,{'fields':[('begin_time','end_time',),('title','classification',),('description',),
('host','speaker',),('room','location',),('attach',)]}),
(_('other info'),{'fields':[('mail_list',),('parent',),('mail_notice','short_message_notice','weixin_notice',)],'classes': ['collapse']})
]
def get_changeform_initial_data(self, request):
now = datetime.datetime.now()
begin = now + datetime.timedelta(hours=12)
end = begin + datetime.timedelta(hours=6)
return {'begin_time':begin,'end_time':end}
admin.site.register(WorkOrder,WorkOrderAdmin)
admin.site.register(Loan,LoanAdmin)
admin.site.register(Reimbursement,ReimbursementAdmin)
admin.site.register(Activity,ActivityAdmin)
+11
View File
@@ -0,0 +1,11 @@
# created at 15-5-23
# coding=utf-8
__author__ = 'zhugl'
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
class MyAppConfig(AppConfig):
name = 'selfhelp'
verbose_name = _("self help")
+330
View File
@@ -0,0 +1,330 @@
# coding=utf-8
import datetime
import os
import xlrd
import decimal
from django.db import transaction
from django.db import models
from django.contrib.auth.models import User
from django.utils.text import force_text
from django.utils.translation import ugettext_lazy as _
from common import generic
from common import const
from mis import settings
from basedata.models import Material,ExtraParam,Project,ExpenseAccount,Measure
from organ.models import OrgUnit
class WorkOrder(generic.BO):
"""
"""
index_weight = 1
code = models.CharField(_("workorder code"),blank=True,null=True,max_length=const.DB_CHAR_CODE_10)
refer = models.ForeignKey("self",verbose_name=_("refer wo"),blank=True,null=True)
title = models.CharField(_("title"),max_length=const.DB_CHAR_NAME_120)
description = models.TextField(_("description"),blank=True,null=True)
business_domain = models.CharField(_("business domain"),max_length=const.DB_CHAR_CODE_4,choices=const.get_value_list('S045'),default='OT')
classification = models.CharField(_("classification"),max_length=const.DB_CHAR_CODE_4,choices=const.get_value_list('S044'),default='D')
service = models.ForeignKey(Material,verbose_name=_("service name"),null=True,blank=True,limit_choices_to={"is_virtual":"1"})
project = models.ForeignKey(Project,verbose_name=_("project"),null=True,blank=True)
status = models.CharField(_("status"),blank=True,null=True,default='NEW',max_length=const.DB_CHAR_CODE_6,choices=const.get_value_list('S046'))
answer = models.TextField(_("answer"),blank=True,null=True)
user = models.ForeignKey(User,verbose_name=_("user"),blank=True,null=True)
attach = models.FileField(_('attach'),blank=True,null=True,help_text=u'工单附件,不导入明细。')
detail = models.FileField(_('to be imported detail'),blank=True,null=True,help_text=u'您可导入需求明细,模板请参考文档FD0007')
def __unicode__(self):
return u"%s-%s" % (self.code,self.title)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
super(WorkOrder,self).save(force_insert,force_update,using,update_fields)
if self.service:
material = self.service
if self.woextravalue_set.count() < 1 and material.extraparam_set and material.extraparam_set.count() > 0:
for param in material.extraparam_set.all():
extra_param = WOExtraValue.objects.create(workorder=self,param_name=param)
self.woextravalue_set.add(extra_param)
item_count = WOItem.objects.filter(workorder=self).count()
if self.detail and item_count == 0:
path = os.path.join(settings.MEDIA_ROOT,self.detail.name)
workbook = xlrd.open_workbook(path)
sheet = workbook.sheet_by_index(0)
row_count = sheet.nrows
with transaction.atomic():
for row_index in range(row_count):
row = sheet.row_values(row_index)
if row_index == 0:
doc_type = row[1]
if doc_type.startswith('0'):
break
else:
continue
elif row_index < 3:
continue
material = None
measure = None
try:
measure = Measure.objects.get(code=row[4])
except Exception,e:
measure = Measure.objects.create(code=row[4],name=force_text(row[5]))
try:
material = Material.objects.get(code=row[0])
except Exception,e:
material = Material(code=row[0],name=force_text(row[1]),spec=force_text(row[2]))
material.save()
WOItem.objects.create(workorder=self,material=material,measure=measure,amount=row[6])
class Meta:
verbose_name = _("workorder apply")
verbose_name_plural = _("workorder apply")
class Media:
js = ('js/workorder.js',)
class WOExtraValue(models.Model):
"""
"""
workorder = models.ForeignKey(WorkOrder,verbose_name=_("workorder"))
param_name = models.ForeignKey(ExtraParam,verbose_name=_("extra param"))
param_value = models.CharField(_("param value"),blank=True,null=True,max_length=const.DB_CHAR_NAME_40)
class Meta:
verbose_name = _("extra value")
verbose_name_plural = _("extra values")
class WOItem(models.Model):
"""
"""
workorder = models.ForeignKey(WorkOrder,verbose_name=_("workorder"))
material = models.ForeignKey(Material,verbose_name=_("material"),null=True,blank=True,limit_choices_to={"is_virtual":"0"})
amount = models.DecimalField(_("amount"),max_digits=10,decimal_places=4,blank=True,null=True)
measure = models.ForeignKey(Measure,verbose_name=_('measure'),blank=True,null=True)
price = models.DecimalField(_("price"),max_digits=10,decimal_places=4,blank=True,null=True)
class Meta:
verbose_name = _("workorder item")
verbose_name_plural = _("workorder items")
class Loan(generic.BO):
"""
"""
LOAD_STATUS = (
('N',_("NEW")),
('I',_("IN PROGRESS")),
('A',_("APPROVED")),
('P',_("PAYED"))
)
index_weight = 3
code = models.CharField(_("loan code"),max_length=const.DB_CHAR_CODE_10,blank=True,null=True)
title = models.CharField(_("title"),max_length=const.DB_CHAR_NAME_120)
description = models.TextField(_("description"),blank=True,null=True)
project = models.ForeignKey(Project,verbose_name=_("project"))
user = models.ForeignKey(User,verbose_name=_("user"),blank=True,null=True)
status = models.CharField(_("status"),blank=True,null=True,default='N',max_length=const.DB_CHAR_CODE_2,choices=LOAD_STATUS)
logout_time = models.DateTimeField(_("logout time"),blank=True,null=True)
loan_amount = models.DecimalField(_("loan amount"),max_digits=10,decimal_places=2,blank=True,null=True)
logout_amount = models.DecimalField(_("logout amount"),max_digits=10,decimal_places=2,blank=True,null=True)
pay_user = models.CharField(_('pay user'),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
pay_time = models.DateTimeField(_('pay time'),blank=True,null=True)
is_clear = models.BooleanField(_('is clear'),default=False)
def __unicode__(self):
import decimal
left = self.loan_amount
left -= self.logout_amount or decimal.Decimal(0.00)
name = '%s%s'%(self.user.last_name,self.user.first_name)
return '%s %s %s J:%.2f Y:%.2f' % (self.code,name,self.title,self.loan_amount,left)
def applier(self):
return u'%s%s'%(self.user.last_name,self.user.first_name)
applier.short_description = _('applier')
def action_pay(self,request):
self.pay_time = datetime.datetime.now()
self.pay_user = request.user.username
self.status = 'P'
self.save()
class Meta:
verbose_name = _("loan")
verbose_name_plural = _("loans")
permissions = (
('financial_pay',_("financial pay")),
)
class Reimbursement(generic.BO):
"""
"""
REIM_STATUS = (
('N',_("NEW")),
('I',_("IN PROGRESS")),
('A',_("APPROVED")),
('P',_("PAYED"))
)
index_weight = 2
code = models.CharField(_("fee code"),max_length=const.DB_CHAR_CODE_10,blank=True,null=True)
title = models.CharField(_("title"),max_length=const.DB_CHAR_NAME_120)
description = models.TextField(_("description"),blank=True,null=True)
project = models.ForeignKey(Project,verbose_name=_("project"))
wo = models.ForeignKey(WorkOrder,verbose_name=_("work order"),null=True,blank=True)
user = models.ForeignKey(User,verbose_name=_("user"),blank=True,null=True)
org = models.ForeignKey(OrgUnit,verbose_name=_("cost center"),blank=True,null=True)
bank_account = models.CharField(_("bank account"),max_length=const.DB_CHAR_NAME_120,blank=True,null=True)
status = models.CharField(_("status"),blank=True,null=True,default='N',max_length=const.DB_CHAR_CODE_2,choices=REIM_STATUS)
amount = models.DecimalField(_("amount of money"),max_digits=10,decimal_places=2,blank=True,null=True)
loan = models.ForeignKey(Loan,verbose_name=_("loan record"),blank=True,null=True)
loan_amount = models.DecimalField(_("loan amount"),max_digits=10,decimal_places=2,blank=True,null=True)
logout_amount = models.DecimalField(_("logout amount"),max_digits=10,decimal_places=2,blank=True,null=True)
pay_amount = models.DecimalField(_("pay amount"),max_digits=10,decimal_places=2,blank=True,null=True)
pay_time = models.DateTimeField(_("pay time"),blank=True,null=True)
pay_user = models.CharField(_('pay user'),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
def applier(self):
return u'%s%s'%(self.user.last_name,self.user.first_name)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
import decimal
if self.loan:
left = self.loan.loan_amount
left -= self.loan.logout_amount or decimal.Decimal(0.00)
if self.logout_amount is None or self.logout_amount == '':
if self.amount < left:
self.logout_amount = self.amount
else:
self.logout_amount = left
self.pay_amount = self.amount
if self.logout_amount:
self.pay_amount -= self.logout_amount
super(Reimbursement,self).save(force_insert,force_update,using,update_fields)
def action_pay(self,request):
if self.loan:
if self.logout_amount is None or self.logout_amount == '':
self.logout_amount = self.amount
if self.logout_amount < 0:
raise Exception(u'核销金额小于0')
if self.loan.logout_amount is None:
self.loan.logout_amount = self.logout_amount
else:
self.loan.logout_amount += self.logout_amount
if self.loan.loan_amount == self.loan.logout_amount:
self.loan.is_clear=True
self.loan.logout_time = datetime.datetime.now()
self.loan.save()
if self.amount > self.logout_amount:
self.pay_amount = self.amount - self.logout_amount
self.pay_time = datetime.datetime.now()
self.pay_user = request.user.username
self.status = 'P'
self.save()
applier.short_description = _('applier')
class Meta:
verbose_name = _("reimbursement")
verbose_name_plural = _("reimbursements")
permissions = (
('financial_pay',_("financial pay")),
)
class ReimbursementItem(models.Model):
"""
"""
import datetime
reimbursement = models.ForeignKey(Reimbursement,verbose_name=_("reimbursement"))
expense_account = models.ForeignKey(ExpenseAccount,verbose_name=_("expenses account"))
begin = models.DateField(_("occur date"),default=datetime.date.today)
amount = models.DecimalField(_("amount of money"),max_digits=10,decimal_places=2)
memo = models.CharField(_("memo"),max_length=const.DB_CHAR_NAME_40,blank=True,null=True)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
super(ReimbursementItem,self).save(force_insert,force_update,using,update_fields)
sql = 'UPDATE selfhelp_reimbursement SET amount = (SELECT SUM(amount) FROM selfhelp_reimbursementitem WHERE reimbursement_id = %s) WHERE id = %s'
params = [self.reimbursement.id,self.reimbursement.id]
generic.update(sql,params)
class Meta:
verbose_name = _("fee item")
verbose_name_plural = _("fee items")
class Activity(generic.BO):
"""
"""
CLASSIFICATION = (
('T',_("Train")),
('M',_("Meeting")),
('G',_("Community")),
)
index_index_weight = 4
begin_time = models.DateTimeField(_('begin time'))
end_time = models.DateTimeField(_('end time'))
enroll_deadline = models.DateTimeField(_('enroll deadline'),blank=True,null=True)
code = models.CharField(_("code"),max_length=const.DB_CHAR_NAME_20,blank=True,null=True)
title = models.CharField(_("title"),max_length=const.DB_CHAR_NAME_120)
parent = models.ForeignKey('self',verbose_name=_("parent"),blank=True,null=True)
description = models.TextField(_("description"),blank=True,null=True)
host = models.CharField(_("host"),max_length=const.DB_CHAR_NAME_80,blank=True,null=True)
speaker = models.CharField(_("speaker"),max_length=const.DB_CHAR_NAME_80,blank=True,null=True)
accept_enroll = models.BooleanField(_("accept enroll"),default=1)
room = models.ForeignKey(Material,verbose_name=_("room"),blank=True,null=True,limit_choices_to={'tp':20})
location = models.CharField(_("location"),max_length=const.DB_CHAR_NAME_80,blank=True,null=True)
classification = models.CharField(_("classification"),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=CLASSIFICATION,default='M')
mail_list = models.TextField(_("mail list"),blank=True,null=True)
mail_notice = models.BooleanField(_("mail notice"),default=1)
short_message_notice = models.BooleanField(_("short message notice"),default=1)
weixin_notice = models.BooleanField(_("weixin notice"),default=1)
status = models.BooleanField(_("published"),default=0)
publish_time = models.DateTimeField(_("publish time"),blank=True,null=True)
attach = models.FileField(_("attach"),blank=True,null=True,upload_to='activity')
class Meta:
verbose_name = _("activity")
verbose_name_plural = _("activities")
class Feedback(models.Model):
"""
"""
RANK = (
('A','A'),
('B','B'),
('C','C'),
('D','D'),
)
activity = models.ForeignKey(Activity)
user = models.ForeignKey(User)
feed_time = models.DateTimeField(_("feedback time"),auto_now_add=True)
rank = models.CharField(_('rank'),max_length=const.DB_CHAR_CODE_2,blank=True,null=True,choices=RANK,default='B')
comment = models.CharField(_("suggest"),blank=True,null=True,max_length=const.DB_CHAR_NAME_80)
class Enroll(models.Model):
"""
"""
activity = models.ForeignKey(Activity)
user = models.ForeignKey(User)
enroll_time = models.DateTimeField(_("enroll time"),auto_now_add=True)
+3
View File
@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.
+6
View File
@@ -0,0 +1,6 @@
from django.conf.urls import include, url,static
import selfhelp.views
urlpatterns = [
url(r"(?P<model>\w+)/(?P<object_id>\d+)/pay", selfhelp.views.pay_action),
]
+46
View File
@@ -0,0 +1,46 @@
# coding=utf-8
from django.contrib.admin import site
from django.contrib.contenttypes.models import ContentType
from django.db import connection
from django.http.response import HttpResponseRedirect
from django.utils.encoding import force_text
from django.template.response import TemplateResponse
from django.contrib import messages
from django.utils.translation import ugettext_lazy as _
from django.contrib.contenttypes.models import ContentType
def pay_action(request,model,object_id):
title = _("Are you sure?")
ct = ContentType.objects.get(app_label='selfhelp',model=model)
obj = ct.get_object_for_this_type(id=int(object_id))
opts = obj._meta
objects_name = force_text(opts.verbose_name)
if model == 'reimbursement':
loan = obj.loan
amount = obj.logout_amount
if loan and (amount is None or amount< 0):
messages.error(request,u'您选择了借款单据,但是未正确填写核销金额,请在\'财务信息\'栏目中更正')
return HttpResponseRedirect("/admin/selfhelp/%s/%s"%(model,object_id))
if request.POST.get("post"):
try:
obj.action_pay(request)
messages.success(request,_('action successfully'))
except Exception,e:
messages.error(request,e)
return HttpResponseRedirect("/admin/selfhelp/%s/%s"%(model,object_id))
context = dict(
site.each_context(request),
title=title,
opts=opts,
objects_name=objects_name,
object=obj,
action_name=_('pay')
)
request.current_app = site.name
return TemplateResponse(request,'admin/invent/stockin/in_confirmation.html', context)
+38
View File
@@ -0,0 +1,38 @@
table#workflow-history{
width:100%
}
table#workflow-history tbody th,table#workflow-history tbody td.col {
width: 16em;
}
.submitlink {
padding-left: 12px;
background: url(../img/icon-yes.gif) 0 .25em no-repeat;
}
a.submitlink:link, a.submitlink:visited {
color: #CC3434;
}
a.submitlink:hover {
color: #993333;
}
label.control{
width:4em;
}
p.next-node{
font-weight:bold;
}
ul.node-users{
list-style:none;
}
p.tooltip{
color:#999;
}
li.done-link{
padding-left: 12px;
background: url(../img/icon_yes.gif) 0 .2em no-repeat;
}
+984
View File
@@ -0,0 +1,984 @@
/* 智捷ERP SmartJet ERP - 1:1 High-Fidelity UI System v4.0 */
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
:root {
--primary: #6366f1;
--primary-light: #818cf8;
--primary-bg: #f5f3ff;
--slate-50: #f8fafc;
--slate-100: #f1f5f9;
--slate-200: #e2e8f0;
--slate-300: #cbd5e1;
--slate-400: #94a3b8;
--slate-500: #64748b;
--slate-600: #475569;
--slate-700: #334155;
--slate-800: #1e293b;
--slate-900: #0f172a;
--success: #10b981;
--warning: #f59e0b;
--danger: #ef4444;
--info: #3b82f6;
--sidebar-width: 240px;
--sidebar-collapsed-width: 0px;
--topbar-height: 64px;
--radius-lg: 16px;
--radius-md: 10px;
--shadow-card: 0 10px 15px -3px rgba(0, 0, 0, 0.04), 0 4px 6px -2px rgba(0, 0, 0, 0.02);
}
*, *::before, *::after {
box-sizing: border-box;
}
body {
background-color: #f1f5f9 !important;
color: var(--slate-800) !important;
font-family: 'Plus Jakarta Sans', -apple-system, sans-serif !important;
margin: 0 !important;
padding: 0 !important;
overflow-x: hidden;
}
#container {
display: flex !important;
min-height: 100vh;
min-width: 0 !important;
max-width: 100vw;
width: 100%;
overflow-x: hidden;
}
/* --- SIDEBAR --- */
#header {
width: var(--sidebar-width) !important;
height: 100vh !important;
position: fixed !important;
left: 0; top: 0;
background: #ffffff !important;
border-right: 1px solid var(--slate-200) !important;
padding: 0 !important;
display: flex !important;
flex-direction: column !important;
z-index: 1000 !important;
color: var(--slate-800) !important;
line-height: normal !important;
overflow: visible !important;
}
/* 覆盖 Django admin 默认深色顶栏链接样式(白字导致侧栏文字不可见) */
#header a:link,
#header a:visited {
color: inherit !important;
text-decoration: none !important;
}
#header a:focus,
#header a:hover {
text-decoration: none !important;
}
.brand-section {
padding: 24px;
display: flex;
align-items: center;
}
.brand-logo {
width: 32px;
height: 32px;
background: linear-gradient(135deg, #6366f1, #a855f7);
border-radius: 8px;
margin-right: 12px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 800;
}
.brand-text h1 {
font-size: 18px !important;
font-weight: 800 !important;
color: #1e293b !important;
margin: 0 !important;
line-height: 1 !important;
}
.brand-text span {
font-size: 11px;
color: var(--slate-400);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.sidebar-nav {
flex: 1;
padding: 0 16px;
overflow-y: auto;
}
.nav-group-label {
padding: 20px 12px 10px 12px;
color: var(--slate-400);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
}
.nav-item {
display: flex;
align-items: center;
padding: 12px 16px;
border-radius: 12px;
color: var(--slate-600) !important;
text-decoration: none !important;
font-weight: 600;
font-size: 14px;
margin-bottom: 4px;
transition: all 0.2s;
}
.nav-item:hover,
.nav-item:focus {
background: var(--slate-50);
color: var(--primary) !important;
}
.nav-item.active,
.nav-item.active:link,
.nav-item.active:visited {
background: var(--primary-bg) !important;
color: var(--primary) !important;
}
.nav-item i { margin-right: 12px; font-size: 18px; width: 20px; text-align: center; }
.nav-item .arrow { margin-left: auto; font-size: 10px; color: var(--slate-300); }
.sidebar-footer {
padding: 16px;
border-top: 1px solid var(--slate-100);
position: relative;
z-index: 1002;
flex-shrink: 0;
background: #ffffff;
}
.nav-item-btn {
width: 100%;
border: none;
background: transparent;
cursor: pointer;
font-family: inherit;
text-align: left;
}
button.nav-item-btn:hover,
button.nav-item-btn:focus {
background: var(--slate-50);
color: var(--primary) !important;
outline: none;
}
/* 侧栏收起 */
#header {
transition: transform 0.25s ease;
}
body.sidebar-collapsed #header {
transform: translateX(-100%);
pointer-events: none;
}
body.sidebar-collapsed #main {
margin-left: 0 !important;
max-width: 100vw !important;
}
/* --- TOPBAR --- */
#main {
flex: 1 1 auto !important;
margin-left: var(--sidebar-width) !important;
width: auto !important;
min-width: 0 !important;
max-width: calc(100vw - var(--sidebar-width)) !important;
overflow-x: hidden;
position: relative;
z-index: 1;
}
.topbar {
height: var(--topbar-height);
background: #ffffff !important;
opacity: 1 !important;
display: flex;
align-items: center;
padding: 0 32px;
position: sticky;
top: 0;
z-index: 900;
box-shadow: 0 1px 2px rgba(0,0,0,0.02);
}
.topbar-left { display: flex; align-items: center; flex: 1; }
.topbar-right {
display: flex;
align-items: center;
gap: 20px;
background: #ffffff !important;
opacity: 1 !important;
position: relative;
z-index: 901;
pointer-events: auto;
}
.breadcrumb-new {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
color: var(--slate-400);
margin-left: 20px;
}
.breadcrumb-new span { color: var(--slate-800); font-weight: 600; }
.search-btn, .icon-btn {
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: var(--slate-500);
cursor: pointer;
transition: background 0.2s;
border: none;
background: transparent;
padding: 0;
font-size: 16px;
line-height: 1;
pointer-events: auto;
position: relative;
z-index: 1;
}
.search-btn:hover, .icon-btn:hover,
.search-btn:focus, .icon-btn:focus {
background: var(--slate-100);
outline: none;
}
.user-menu-wrap {
position: relative;
padding-left: 20px;
border-left: 1px solid var(--slate-200);
}
.user-profile {
display: flex;
align-items: center;
gap: 12px;
padding: 4px 8px;
border: none;
border-radius: 10px;
background: #ffffff !important;
opacity: 1 !important;
cursor: pointer;
font-family: inherit;
pointer-events: auto;
}
.user-profile:hover,
.user-profile:focus {
background: var(--slate-50) !important;
outline: none;
}
.user-caret {
font-size: 10px;
color: var(--slate-400);
margin-left: 4px;
}
.user-dropdown {
display: none;
position: absolute;
top: calc(100% + 8px);
right: 0;
min-width: 140px;
background: #ffffff;
border: 1px solid var(--slate-200);
border-radius: 10px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
padding: 6px;
z-index: 1100;
}
.user-dropdown.open {
display: block;
}
.user-dropdown a {
display: block;
padding: 10px 14px;
border-radius: 8px;
color: var(--slate-700) !important;
text-decoration: none !important;
font-size: 13px;
font-weight: 600;
}
.user-dropdown a:hover {
background: var(--slate-50);
color: var(--primary) !important;
}
.avatar {
width: 36px;
height: 36px;
border-radius: 10px;
background: var(--slate-200);
flex-shrink: 0;
opacity: 1 !important;
}
.user-info { display: flex; flex-direction: column; background: transparent; }
.user-name { font-weight: 700; font-size: 13px; color: var(--slate-800) !important; opacity: 1 !important; }
.user-role { font-size: 11px; color: var(--slate-500) !important; opacity: 1 !important; }
/* --- CONTENT --- */
#content {
padding: 32px !important;
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
position: relative;
z-index: 1;
}
.dashboard #content {
width: 100% !important;
max-width: 100% !important;
}
#content-main {
float: none !important;
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
}
.dashboard #content > h1 {
display: none;
}
#content > h1 {
font-size: 22px;
font-weight: 700;
color: var(--slate-900);
margin: 0 0 24px;
padding-right: 120px;
}
.colM, .colMS {
max-width: 100%;
min-width: 0;
}
#changelist, #changelist-form, .results {
max-width: 100%;
overflow-x: auto;
}
/* --- STATS CARDS --- */
.stats-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 24px;
margin-bottom: 32px;
width: 100%;
max-width: 100%;
}
.stat-card {
background: #ffffff;
padding: 24px;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
position: relative;
min-width: 0;
}
.stat-header { display: flex; justify-content: space-between; margin-bottom: 12px; }
.stat-icon-bg { width: 40px; height: 40px; border-radius: 12px; display: flex; align-items: center; justify-content: center; color: white; font-size: 18px; }
.stat-info-icon { color: var(--slate-300); font-size: 14px; cursor: help; }
.stat-value { font-size: 24px; font-weight: 800; color: var(--slate-900); margin-bottom: 8px; }
.stat-label { font-size: 14px; color: var(--slate-500); font-weight: 500; }
.stat-footer { display: flex; align-items: center; justify-content: space-between; margin-top: 16px; border-top: 1px solid var(--slate-50); padding-top: 16px; }
.stat-trend { font-size: 12px; font-weight: 600; display: flex; align-items: center; gap: 4px; }
.trend-up { color: var(--success); }
.trend-down { color: var(--danger); }
.sparkline { width: 60px; height: 30px; }
/* --- DATA MODULES --- */
.dashboard-main-grid {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(260px, 340px);
gap: 24px;
width: 100%;
max-width: 100%;
}
.module-card {
background: white;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
overflow: hidden;
display: flex;
flex-direction: column;
min-width: 0;
}
.card-title {
padding: 24px 32px;
font-size: 16px;
font-weight: 700;
color: var(--slate-900);
display: flex;
justify-content: space-between;
align-items: center;
}
/* --- TABLE STYLES --- */
.table-container { overflow-x: auto; max-width: 100%; padding: 0 12px 12px 12px; }
table { width: 100%; max-width: 100%; border-collapse: collapse; }
th { text-align: left; padding: 12px 20px; font-size: 12px; color: var(--slate-400); font-weight: 600; border-bottom: 1px solid var(--slate-100); }
td { padding: 16px 20px; font-size: 13px; color: var(--slate-700); border-bottom: 1px solid var(--slate-50); }
tr:last-child td { border: none; }
.badge {
padding: 4px 10px;
border-radius: 6px;
font-size: 11px;
font-weight: 700;
}
.badge-new { background: #eef2ff; color: #6366f1; }
.badge-audit { background: #ecfdf5; color: #10b981; }
.badge-update { background: #eff6ff; color: #3b82f6; }
.badge-submit { background: #fff7ed; color: #f97316; }
/* --- TASK LIST --- */
.task-list { padding: 0 24px 24px 24px; }
.task-item {
display: flex;
align-items: flex-start;
gap: 16px;
padding: 16px;
border-radius: 12px;
margin-bottom: 12px;
background: #ffffff;
border: 1px solid var(--slate-50);
transition: all 0.2s;
}
.task-item:hover { border-color: var(--slate-200); background: var(--slate-50); }
.task-icon { width: 36px; height: 36px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 16px; color: white; }
.task-content { flex: 1; }
.task-name { font-size: 13px; font-weight: 700; color: var(--slate-800); }
.task-info { font-size: 11px; color: var(--slate-400); margin-top: 2px; }
.task-time { font-size: 11px; color: var(--slate-400); }
.priority-tag { font-size: 10px; font-weight: 800; padding: 2px 6px; border-radius: 4px; text-transform: uppercase; margin-top: 8px; display: inline-block; }
.p-urgent { background: #fef2f2; color: #ef4444; }
.p-high { background: #fff7ed; color: #f97316; }
.p-medium { background: #fffbeb; color: #f59e0b; }
.p-low { background: #f0fdf4; color: #10b981; }
.view-all { text-align: center; padding: 20px; border-top: 1px solid var(--slate-50); }
.view-all a { color: var(--primary); text-decoration: none; font-size: 13px; font-weight: 700; }
/* --- ADMIN FORM ACTIONS --- */
#content-main {
position: relative;
padding-bottom: 24px;
}
.object-tools {
float: none !important;
position: absolute !important;
top: 32px;
right: 32px;
margin: 0 !important;
padding: 0 !important;
font-size: 12px !important;
font-weight: 600 !important;
opacity: 1 !important;
z-index: 5;
pointer-events: auto;
}
.object-tools li {
height: auto !important;
margin-left: 0 !important;
}
.object-tools a:link,
.object-tools a:visited,
.object-tools a.historylink {
display: inline-flex !important;
align-items: center;
float: none !important;
padding: 8px 16px !important;
background: var(--slate-600) !important;
color: #ffffff !important;
opacity: 1 !important;
border-radius: 8px !important;
font-size: 12px !important;
font-weight: 600 !important;
text-transform: none !important;
letter-spacing: 0 !important;
text-decoration: none !important;
}
.object-tools a:hover,
.object-tools a:focus {
background: var(--slate-700) !important;
color: #ffffff !important;
opacity: 1 !important;
}
.submit-row {
display: flex !important;
flex-wrap: wrap;
align-items: center;
justify-content: flex-end;
gap: 10px;
overflow: visible !important;
padding: 16px 20px !important;
margin: 24px 0 0 !important;
background: #ffffff !important;
border: 1px solid var(--slate-200) !important;
border-radius: var(--radius-md) !important;
box-shadow: var(--shadow-card);
}
.submit-row p {
margin: 0 !important;
}
.submit-row p.deletelink-box {
float: none !important;
margin-right: auto !important;
}
.submit-row a.deletelink {
display: inline-flex !important;
align-items: center;
justify-content: center;
height: auto !important;
min-height: 38px;
line-height: 1.4 !important;
padding: 10px 20px !important;
border-radius: 8px !important;
color: #ffffff !important;
background: var(--danger) !important;
text-decoration: none !important;
font-weight: 600;
font-size: 13px;
}
.submit-row a.deletelink:hover,
.submit-row a.deletelink:focus {
background: #dc2626 !important;
}
.submit-row input,
.submit-row a.button,
.submit-row .button {
height: auto !important;
min-height: 38px;
line-height: 1.4 !important;
padding: 10px 20px !important;
margin: 0 !important;
border-radius: 8px !important;
font-size: 13px !important;
font-weight: 600 !important;
text-transform: none !important;
cursor: pointer;
}
.submit-row input.default {
background: var(--primary) !important;
border-color: var(--primary) !important;
color: #ffffff !important;
}
.submit-row input[name="_addanother"],
.submit-row input[name="_continue"],
.submit-row input[name="_saveasnew"] {
background: #ffffff !important;
color: var(--slate-700) !important;
border: 1px solid var(--slate-300) !important;
}
/* 双栏选择器 */
.selector {
max-width: 100%;
}
.selector .selector-available h2,
.selector .selector-chosen h2 {
font-size: 13px;
font-weight: 600;
color: var(--slate-700);
}
.selector select {
max-width: 100%;
}
.selector .selector-filter label {
font-size: 12px;
color: var(--slate-500);
}
/* 表单模块 */
.module h2, fieldset.module h2 {
font-size: 14px;
font-weight: 700;
color: var(--slate-800);
background: var(--slate-50) !important;
border-bottom: 1px solid var(--slate-200);
}
.form-row label {
color: var(--slate-700) !important;
font-weight: 600;
}
/* Hide messy native elements */
#footer, .breadcrumbs, #content-related { display: none !important; }
@media (max-width: 1280px) {
.stats-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.dashboard-main-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.stats-grid {
grid-template-columns: 1fr;
}
#content {
padding: 20px !important;
}
.topbar {
padding: 0 16px;
}
}
/* --- LOGIN PAGE --- */
body.login-page {
margin: 0;
padding: 0;
min-height: 100vh;
background: var(--slate-100);
overflow-x: hidden;
}
.login-shell {
display: flex;
min-height: 100vh;
width: 100%;
}
.login-brand {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 48px;
background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 50%, #a855f7 100%);
color: #ffffff;
position: relative;
overflow: hidden;
}
.login-brand::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(circle at 20% 80%, rgba(255,255,255,0.12) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(255,255,255,0.08) 0%, transparent 40%);
pointer-events: none;
}
.login-brand-inner {
position: relative;
max-width: 420px;
z-index: 1;
}
.login-brand-logo {
width: 56px;
height: 56px;
background: rgba(255, 255, 255, 0.2);
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
margin-bottom: 24px;
backdrop-filter: blur(4px);
}
.login-brand h1 {
font-size: 36px;
font-weight: 800;
margin: 0 0 8px;
letter-spacing: -0.02em;
}
.login-brand-tagline {
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.1em;
opacity: 0.8;
margin: 0 0 20px;
}
.login-brand-desc {
font-size: 16px;
opacity: 0.9;
margin: 0 0 32px;
line-height: 1.6;
}
.login-brand-features {
list-style: none;
padding: 0;
margin: 0;
}
.login-brand-features li {
padding: 10px 0;
padding-left: 24px;
position: relative;
font-size: 14px;
opacity: 0.9;
}
.login-brand-features li::before {
content: '✓';
position: absolute;
left: 0;
font-weight: 700;
}
.login-panel {
flex: 0 0 480px;
display: flex;
align-items: center;
justify-content: center;
padding: 32px;
background: var(--slate-50);
}
.login-card {
width: 100%;
max-width: 400px;
background: #ffffff;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
padding: 40px 36px;
border: 1px solid var(--slate-200);
}
.login-card-header {
margin-bottom: 28px;
}
.login-card-header h2 {
font-size: 24px;
font-weight: 800;
color: var(--slate-900);
margin: 0 0 8px;
}
.login-card-header p {
font-size: 14px;
color: var(--slate-500);
margin: 0;
}
.login-error {
background: #fef2f2;
color: #dc2626;
padding: 12px 14px;
border-radius: 8px;
font-size: 13px;
margin-bottom: 20px;
border: 1px solid #fecaca;
}
.login-form {
margin-bottom: 24px;
}
.login-field {
margin-bottom: 20px;
}
.login-field label {
display: block;
font-size: 13px;
font-weight: 600;
color: var(--slate-700);
margin-bottom: 8px;
}
.login-field input[type="text"],
.login-field input[type="password"] {
width: 100%;
padding: 12px 14px;
font-size: 14px;
font-family: inherit;
border: 1px solid var(--slate-300);
border-radius: var(--radius-md);
background: #ffffff;
color: var(--slate-800);
box-sizing: border-box;
transition: border-color 0.2s, box-shadow 0.2s;
}
.login-field input[type="text"]:focus,
.login-field input[type="password"]:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px var(--primary-bg);
}
.login-submit {
width: 100%;
padding: 14px;
margin-top: 8px;
font-size: 15px;
font-weight: 700;
font-family: inherit;
color: #ffffff;
background: linear-gradient(135deg, var(--primary), #7c3aed);
border: none;
border-radius: var(--radius-md);
cursor: pointer;
transition: opacity 0.2s, transform 0.15s;
}
.login-submit:hover {
opacity: 0.92;
transform: translateY(-1px);
}
.login-submit:active {
transform: translateY(0);
}
.login-demo {
background: var(--primary-bg);
border: 1px solid #e0e7ff;
border-radius: var(--radius-md);
padding: 16px;
margin-bottom: 20px;
}
.login-demo-title {
font-size: 12px;
font-weight: 700;
color: var(--primary);
margin-bottom: 8px;
}
.login-demo-row {
display: flex;
align-items: center;
gap: 12px;
font-size: 13px;
color: var(--slate-600);
margin-bottom: 4px;
}
.login-demo-row span {
min-width: 48px;
color: var(--slate-500);
}
.login-demo-row code {
font-family: 'Consolas', monospace;
background: rgba(255, 255, 255, 0.8);
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
color: var(--slate-800);
}
.login-demo-divider {
height: 1px;
background: #e0e7ff;
margin: 12px 0;
}
.login-footer-link {
text-align: center;
}
.login-footer-link a {
font-size: 12px;
color: var(--slate-400);
text-decoration: none;
}
.login-footer-link a:hover {
color: var(--primary);
}
@media (max-width: 900px) {
.login-shell {
flex-direction: column;
}
.login-brand {
flex: none;
padding: 32px 24px;
}
.login-brand-inner {
max-width: 100%;
text-align: center;
}
.login-brand-logo {
margin-left: auto;
margin-right: auto;
}
.login-brand-features {
display: none;
}
.login-panel {
flex: 1;
padding: 24px 16px 32px;
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 B

+22
View File
@@ -0,0 +1,22 @@
/**
author:zhugl
date:2015-05-15
*/
(function($) {
$(document).ready(function() {
/**
set operation type
*/
$("fieldset.workflow input[type='radio']").on('click',function(){
operation = $(this).val();
next = "approve/"+operation;
$("#workflow_approve").attr("href",next);
});
try{
$("div.inline-group table tbody tr.form-row").removeClass('has_original');
$("div.inline-group table tbody tr.form-row td:first-child").find('p').hide();
}catch(e){
}
});
})(django.jQuery);
+86
View File
@@ -0,0 +1,86 @@
(function () {
'use strict';
function ready(fn) {
if (document.readyState !== 'loading') {
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
function toggleSidebar() {
var collapsed = document.body.classList.toggle('sidebar-collapsed');
try {
localStorage.setItem('erp-sidebar-collapsed', collapsed ? '1' : '0');
} catch (e) {}
updateCollapseLabel();
}
function updateCollapseLabel() {
var btn = document.getElementById('sidebarCollapseBtn');
if (!btn) {
return;
}
var collapsed = document.body.classList.contains('sidebar-collapsed');
btn.innerHTML = collapsed
? '<i>→</i> 展开菜单'
: '<i>←</i> 收起菜单';
}
function restoreSidebarState() {
try {
if (localStorage.getItem('erp-sidebar-collapsed') === '1') {
document.body.classList.add('sidebar-collapsed');
}
} catch (e) {}
updateCollapseLabel();
}
function toggleUserMenu() {
var menu = document.getElementById('userDropdown');
if (!menu) {
return;
}
var open = menu.classList.toggle('open');
if (open) {
document.addEventListener('click', closeUserMenuOutside);
} else {
document.removeEventListener('click', closeUserMenuOutside);
}
}
function closeUserMenuOutside(e) {
var menu = document.getElementById('userDropdown');
var profile = document.getElementById('userProfileToggle');
if (!menu || !profile) {
return;
}
if (!menu.contains(e.target) && !profile.contains(e.target)) {
menu.classList.remove('open');
document.removeEventListener('click', closeUserMenuOutside);
}
}
function bindClick(id, handler) {
var el = document.getElementById(id);
if (el) {
el.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
handler(e);
});
}
}
ready(function () {
restoreSidebarState();
bindClick('topbarMenuBtn', toggleSidebar);
bindClick('sidebarCollapseBtn', toggleSidebar);
bindClick('topbarNotifyBtn', function () {
window.location.href = '/admin/workflow/todolist/';
});
bindClick('userProfileToggle', toggleUserMenu);
});
})();
+11
View File
@@ -0,0 +1,11 @@
/**
* Created by Administrator on 15-5-23.
*/
(function($) {
$(document).ready(function() {
/**
set operation type
*/
$("#woextravalue_set-group select").attr('readonly','true')
});
})(django.jQuery);
+2
View File
@@ -0,0 +1,2 @@
default_app_config = 'syscfg.apps.SysConfig'
+53
View File
@@ -0,0 +1,53 @@
# coding=utf-8
from django.contrib import admin
from django.forms import ModelForm,DateField
from syscfg.models import *
from common import generic
class SiteForm(ModelForm):
"""
"""
class Meta:
model = Site
fields = '__all__'
class SiteAdmin(admin.ModelAdmin):
list_per_page = 10
list_display = ['name', 'begin', 'end']
fields = (('begin', 'end'), 'name', 'description', 'user')
filter_horizontal = ['user']
form = SiteForm
class ModuleAdmin(generic.BOAdmin):
CODE_NUMBER_WIDTH = 3
CODE_PREFIX = 'U'
list_display = ['code','name','parent','status']
ordering = ['weight']
raw_id_fields = ['parent']
class MenuAdmin(generic.BOAdmin):
CODE_NUMBER_WIDTH = 3
CODE_PREFIX = 'M'
list_display = ['code','name','module','status']
list_filter = ['module']
ordering = ['weight']
raw_id_fields = ['module']
class RoleAdmin(generic.BOAdmin):
CODE_NUMBER_WIDTH = 3
CODE_PREFIX = 'R'
list_display = ['code','name','status']
filter_horizontal = ['users','menus']
admin.site.register(Site, SiteAdmin)
admin.site.register(Module,ModuleAdmin)
admin.site.register(Menu,MenuAdmin)
admin.site.register(Role,RoleAdmin)
+9
View File
@@ -0,0 +1,9 @@
__author__ = 'zhugl'
# created at 15-4-22
from django.apps.config import AppConfig
from django.utils.translation import ugettext_lazy as _
class SysConfig(AppConfig):
name = 'syscfg'
verbose_name = _('SysConfig')
+79
View File
@@ -0,0 +1,79 @@
# coding=utf-8
from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from common import const
from common import generic
class Site(models.Model):
"""
站点一个站点下可有多个公司处于同一个站点下的用户逻辑上位于同一个组织
"""
index_weight = 1
begin = models.DateField(_('begin date'), blank=True,null=True)
end = models.DateField(_('end date'), blank=True,null=True)
name = models.CharField(_('site name'), max_length=const.DB_CHAR_NAME_40)
description = models.TextField(_('site description'),blank=True,null=True)
user = models.ManyToManyField(User,verbose_name=_('administrator'))
def __unicode__(self):
return u'%s'%self.name
class Meta:
verbose_name = _('Site')
verbose_name_plural = _('Site')
class Module(generic.BO):
"""
模块管理
"""
index_weight = 2
code = models.CharField(_("module code"),max_length=const.DB_CHAR_CODE_6,blank=True,null=True)
name = models.CharField(_("module name"),max_length=const.DB_CHAR_NAME_40)
url = models.URLField(_("module url"),blank=True,null=True,max_length=const.DB_CHAR_NAME_80)
weight = models.IntegerField(_("weight"),blank=True,null=True,default=99)
icon = models.CharField(_("style class"),blank=True,null=True,max_length=const.DB_CHAR_NAME_40)
parent = models.ForeignKey('self',blank=True,null=True,verbose_name=_("parent"))
status = models.BooleanField(_("in use"),default=True)
class Meta:
verbose_name = _("module")
verbose_name_plural = _("module")
class Menu(generic.BO):
"""
菜单管理
"""
index_weight = 3
module = models.ForeignKey(Module,verbose_name=_("module"))
code = models.CharField(_("menu code"),max_length=const.DB_CHAR_CODE_6,blank=True,null=True)
name = models.CharField(_("menu name"),max_length=const.DB_CHAR_NAME_40)
url = models.URLField(_("menu url"),blank=True,null=True,max_length=const.DB_CHAR_NAME_80)
weight = models.IntegerField(_("weight"),blank=True,null=True,default=99)
icon = models.CharField(_("style class"),blank=True,null=True,max_length=const.DB_CHAR_NAME_40)
status = models.BooleanField(_("in use"),default=True)
class Meta:
verbose_name = _("menu")
verbose_name_plural = _("menu")
class Role(generic.BO):
"""
角色管理分配用户所拥有的菜单
"""
index_weight = 4
code = models.CharField(_("role code"),max_length=const.DB_CHAR_CODE_6,blank=True,null=True)
name = models.CharField(_("role name"),max_length=const.DB_CHAR_NAME_40)
description = models.CharField(_("description"),max_length=const.DB_CHAR_NAME_80,blank=True,null=True)
status = models.BooleanField(_("in use"),default=True)
parent = models.ForeignKey('self',blank=True,null=True,verbose_name=_("parent"))
users = models.ManyToManyField(User,verbose_name=_("role users"),blank=True)
menus = models.ManyToManyField(Menu,verbose_name=_("role menus"),blank=True)
class Meta:
verbose_name = _("role")
verbose_name_plural = _("role")
+3
View File
@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

Some files were not shown because too many files have changed in this diff Show More