mirror of
https://github.com/GreaterWMS/GreaterWMS.git
synced 2026-06-19 07:36:00 +00:00
change
Former-commit-id: 1ce264e768be19765848e792a2a369e4bcf5ab22 [formerly 835a3b2abcc7d02ce4501ca12e4fe641aa7c45e5] [formerly 43c8461f948d9ba930aba59f7b93e08af37917ec [formerly ecd43f50bd1f11e3ca841096e309f9052f73be46]] [formerly 9dad89c6c83a4f5d29fcbb41ba71021a9c5f278d [formerly 8e839f4f42b2ed01b6b31755d1d0c5c65d448a8f] [formerly e70e01e1731bbe9ff022d8f4ab917da435e10981 [formerly a6ef0cf96d3b4ad3173569c3034d435b68bc61fb]]] [formerly 7bd03c373e494a773d59468b1bb8c139e4780890 [formerly 6979daf92b6a517e0c11070e6fb724654c28358b] [formerly 7bd03c373e494a773d59468b1bb8c139e4780890 [formerly 6979daf92b6a517e0c11070e6fb724654c28358b] [formerly b9b738f8675c3b22a77f6b191c4a06cc23f31b1d [formerly 53ec5b6de9b8d1199377ac15c08f9d62ae5b8444]]]] [formerly 8278d8b0303b2c4a1da42e6594694bd70615bfff [formerly 5af8152a882db06050a7dd31eb6e8a12d6c62dd3] [formerly 8278d8b0303b2c4a1da42e6594694bd70615bfff [formerly 5af8152a882db06050a7dd31eb6e8a12d6c62dd3] [formerly efe021b0d1996a738668af58cfc7da5993462ff4 [formerly db301edd777df6601a416ccc049f9db2c19b4292]]] [formerly 7bd03c373e494a773d59468b1bb8c139e4780890 [formerly 6979daf92b6a517e0c11070e6fb724654c28358b] [formerly b9b738f8675c3b22a77f6b191c4a06cc23f31b1d [formerly 53ec5b6de9b8d1199377ac15c08f9d62ae5b8444]] [formerly b9b738f8675c3b22a77f6b191c4a06cc23f31b1d [formerly 53ec5b6de9b8d1199377ac15c08f9d62ae5b8444] [formerly 0b669f5694112ae03fe33d4df5fc80338db90035 [formerly 8eebf943a86c60a8d1ddda4df331d89f6f359683]]]]] [formerly 4a05aa1c5b584af4e70cbd24828cc0dc2fa6a249 [formerly 8e3d6065566b6feda200b0c2920b449ce04a4f8d] [formerly 4a05aa1c5b584af4e70cbd24828cc0dc2fa6a249 [formerly 8e3d6065566b6feda200b0c2920b449ce04a4f8d] [formerly 0167dd77e345ab95dbea33dafb157856d99cfd02 [formerly e11a0c94b6853554b8ee0b01382aef49b1830d01]]] [formerly 080ed9b8dda9038c695fb15d84a80bf6fed13ec4 [formerly 93655f5d1d39c146038b8185703017d2fe2d7a3b]] [formerly b1113cd5d16436f0c0f64b8338ed23ecae131515 [formerly c6e057881b651cb243e73609db6b9b4bed51a878] [formerly b1113cd5d16436f0c0f64b8338ed23ecae131515 [formerly c6e057881b651cb243e73609db6b9b4bed51a878] [formerly 167fb27fba78a5f698441901097916581c6404b0 [formerly 2b7b66d03e3d0e55b4aacca370b347e89740817b]]] [formerly 080ed9b8dda9038c695fb15d84a80bf6fed13ec4 [formerly 93655f5d1d39c146038b8185703017d2fe2d7a3b] [formerly 080ed9b8dda9038c695fb15d84a80bf6fed13ec4 [formerly 93655f5d1d39c146038b8185703017d2fe2d7a3b] [formerly c00d1b174e6232be1054873a03ca8321af28472e [formerly ba37d6315e15212ee818e728e7acdbde2454dc07 [formerly aa8f50ecc422ecd5a8816823128fdbf004e212a5] [formerly 81014c254ca2d5bbdb3ea5cd0cfecf220e7d319a [formerly 986fa9b65ab1acd22ea51ba91aa9fe48be2d1253]] [formerly e2adef2d3c2a610aba0b3e8afdf6a206ab1b6157 [formerly 8333b363032ec2038a6d08067246fb133b0d2885] [formerly 0fd360620afd8ec8484bce5c921feb2a7a7d37d9]] [formerly 0fd360620afd8ec8484bce5c921feb2a7a7d37d9 [formerly 3fd364606d971551f50ab5297eed680d51dc60c1]] [formerly b7162540d4bf13a6990d488bc5763429f5ea209b [formerly 0fd360620afd8ec8484bce5c921feb2a7a7d37d9 [formerly 3fd364606d971551f50ab5297eed680d51dc60c1] [formerly b7162540d4bf13a6990d488bc5763429f5ea209b [formerly d26c38804c560be5d9e7f051766e71539c128f10 [formerly cf1f0e0c58bbabdbc2735c8a7fd85b07bc601845] [formerly b7162540d4bf13a6990d488bc5763429f5ea209b [formerlyf04f0e33a5]]]]]]]]]]]] Former-commit-id: ec50f9de7f1b5d56747338a018f88fc5228567f1 [formerly 404933efe07b8fb158a3a7b03bde648afee9df80] [formerly 6a665fa2ae3a6c47a99c40000e7b6864592a7dac [formerly f700e146855839d85aacc60644fc02b64478a3ba]] [formerly 0a894c11a40eeccf4a570a26ef93343797fc36d5 [formerly 1599df40c0f2b6a51af13fb85f5e9edb485a6802] [formerly a483e7f693f410cac224400d1228af4ca8e81f8c [formerly 45e3f2099d96a32983bdfeb5e0c4eeeee8bae743]]] [formerly ff96f1c62461efe685c3f8069ed02a60e7fd20fd [formerly 597b9b9028d5879f2ee4ef9e1157697bcb2c4689]] [formerly 4f3939a02eaf94745ee59cae2c377ba01bfdd0a2 [formerly 14acbdd1e884559f66f1d8ddb0cbdcee8fa2af1e] [formerly 4f3939a02eaf94745ee59cae2c377ba01bfdd0a2 [formerly 14acbdd1e884559f66f1d8ddb0cbdcee8fa2af1e] [formerly aa59ff3c18eef08aa6d0f59b098619858527800b [formerly fea1d34dd71239752c3ce58b63881211ce2bd18c]]] [formerly ff96f1c62461efe685c3f8069ed02a60e7fd20fd [formerly 597b9b9028d5879f2ee4ef9e1157697bcb2c4689] [formerly ff96f1c62461efe685c3f8069ed02a60e7fd20fd [formerly 597b9b9028d5879f2ee4ef9e1157697bcb2c4689] [formerly ba963f2b1b38ef24fa5a6f506bb5105de672a08f [formerly 577bf2a6a365de54bd7fc306274fff2135deb991]]]]] Former-commit-id: eea674bfc0f88b7d8ce45e76956c10a687e71642 [formerly cec885680849b1af76998d4fb19d3056de7ee85b] [formerly 9eb89164bea30a7b45f63248a4b368d7bfc18d69 [formerly ecc6ed08365110ab03d38c8d78652635aecdf9f4]] [formerly 9eb89164bea30a7b45f63248a4b368d7bfc18d69 [formerly ecc6ed08365110ab03d38c8d78652635aecdf9f4] [formerly 32e2aa48b5d475d10fc30712ac18ee9a9c097533 [formerly a7b98f94df3f3dad11f19883691d42621d6ebac4]]] [formerly 4a2b62e5587f0622136c41de0725dfabe963e759 [formerly c1b9a4193d558d3186aed1611dd43df9dd9ec813] [formerly 4a2b62e5587f0622136c41de0725dfabe963e759 [formerly c1b9a4193d558d3186aed1611dd43df9dd9ec813] [formerly 0d8c4276a122a1ea22d5049058e44bdb497f9d64 [formerly ad49c5a35584cec535208eeb552f9b986ae92ab3]]]] Former-commit-id: 4a2b62e5587f0622136c41de0725dfabe963e759 [formerly c1b9a4193d558d3186aed1611dd43df9dd9ec813] Former-commit-id: 273c338308585e5b5066c46de4b9ba99fccb0e49 [formerly 9d136b4ce28189a743e36273c03c9c85c57936a0] [formerly 2ea7c121e6b19f89cd7dae4fbc5b1cf2fdaaea13 [formerly f99a7cdd51c969579cec97a07e80f3d20fe1caef]] Former-commit-id: dc9a556bf8f17ca69fc9f008bbd11f2469d08df3 [formerly 87c99130bf6a4612a6329cf0cb658a522d9e9b10] Former-commit-id: 1540286f7a4f6a7ff234ef84a2188c3e21a11a15 Former-commit-id: e1b4f6fce12993e8a9b9b6645fdc1f0b457e8558 [formerly b086d6a2574172e8651165f7e54ec23c869bb5c8] Former-commit-id: d2fa79438ae5f490990e2e30f23307dbcc1b0557 Former-commit-id:7211af68ca[formerly 1c2ad6eb8608ac0d463f41d5d8e3c31e31badace] [formerly 3a517145e23d33fae7e18e2f97453d7c77f4f3fb [formerly 66e42a6a5eae1d16d85b61fac274b37f038db72c]] [formerly 08b8e7d564824db98df493d47c16823e490ad388] [formerly ac51e50dd8af9ac51b02b9a9dfd6ef097d31a512 [formerly 54f33b056777e170777c66f63635c026cb2f4502 [formerly 4e52795127b5b1398a5195c5d93b9e87471861e5] [formerly aaff0707950912c85783267280fd38c92dcebb20 [formerly a29f989feca024cfc0c70db3e80240d7cfa604ba]]]] Former-commit-id: b3b10e94bb3fada54e515ebbb430c6f8d186da04 [formerly fbf0518e2b8258c3b8459aa0c686e708fb93c27f [formerly 22d10feca8741db1b4d3174a8939b4ae8b5dbbb7] [formerly d88eaf73ca79f73ee58c92a95166874b8dce4667 [formerly 6fe9c8b17e57c68d3b9941a478ffc2a5c996c48a]]] Former-commit-id: 70d26085f499e64417ecf0e6637e5a6cf355e794 Former-commit-id: 041f98dc0b352494b6b82d90ec476dc6aabf9f32 [formerly ff5484880b2bf6c5e1c00dc41a3458d50e020786] Former-commit-id: ca826858f49845c7dd7f56946b2b2483b3207b58
This commit is contained in:
@@ -1 +0,0 @@
|
||||
*.vue linguist-language=python
|
||||
@@ -1,12 +0,0 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: ['paypal.me/singosgu/1usd']
|
||||
@@ -1,8 +0,0 @@
|
||||
node_modules/
|
||||
__pycache__/
|
||||
authorization.txt
|
||||
authorization.json
|
||||
db.sqlite3
|
||||
.idea/
|
||||
delete.py
|
||||
*.xlsx
|
||||
@@ -1,40 +0,0 @@
|
||||
~~~shell
|
||||
如何配置Cordova环境,请参考
|
||||
http://www.quasarchs.com/quasar-cli/developing-cordova-apps/preparation
|
||||
|
||||
# 进入templates目录,创建src-cordova
|
||||
quasar mode add cordova
|
||||
# 进入src-cordova目录
|
||||
cd src-cordova
|
||||
# 部署安卓客户端
|
||||
cordova platform add android
|
||||
输入app名称为org.greaterwms.app
|
||||
# 返回templates目录,先启动一次项目来创建gradle文件夹
|
||||
quasar d -m cordova -T android
|
||||
Ctrl + c退出
|
||||
# 修改gradle版本下载distributionUrl,文件在tempates/src-cordova/platforms/android/gradle/wrapper/gradle-wrapper.properties
|
||||
distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-4.10.3-all.zip
|
||||
# 进入src-cordova目录来安装所需要的插件
|
||||
cd src-cordova
|
||||
# 按顺序安装插件
|
||||
cordova plugin add cordova-plugin-device
|
||||
cordova plugin add cordova-plugin-battery-status
|
||||
cordova plugin add cordova-plugin-camera
|
||||
cordova plugin add com-darryncampbell-cordova-plugin-intent
|
||||
cordova plugin add https://gitee.com/Singosgu/cordova-plugin-ubx.git
|
||||
# 修改适配问题的文件,templates\src-cordova\platforms\android\app\src\main\java\org\apache\cordova\camera\FileProvider.java
|
||||
第21行的android.support.v4.content.FileProvider,修改为androidx.core.content.FileProvider
|
||||
# 修改适配问题的文件,templates\src-cordova\platforms\android\app\src\main\java\org\apache\cordova\camera\CameraLauncher.java
|
||||
第42行的android.support.v4.content.FileProvider,修改为androidx.core.content.FileProvider
|
||||
# 回到templates,再次启动项目
|
||||
quasar d -m cordova -T android
|
||||
|
||||
现在只支持Zebra扫描枪,Zebra扫描枪需要打开广播
|
||||
Zebra扫描枪配置请看:
|
||||
https://github.com/Singosgu/GreaterWMS/blob/master/Zebra-Profile.pdf
|
||||
|
||||
# 打包apk
|
||||
quasar build -m android
|
||||
|
||||
接下来在dist/cordova/android/apk/release下面可以找到一个未签名的apk,关于怎么签名,自行百度
|
||||
~~~
|
||||
@@ -1,71 +0,0 @@
|
||||
~~~shell
|
||||
su - // 进入管理员账号
|
||||
yum update
|
||||
yum upgrade
|
||||
yum -y install gcc-c++ // 安装依赖
|
||||
yum install zlib-devel xz-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel libffi-devel gcc make // 安装python依赖
|
||||
yum install nodejs // 安装nodejs
|
||||
npm install n -g // 安装n
|
||||
/usr/local/bin/n 14.18.3 // 升级nodejs
|
||||
npm install npm -g // 升级npm
|
||||
npm install yarn -g // 安装yarn
|
||||
npm install @quasar/cli -g // 安装quasar
|
||||
npm install cordova -g // 安装cordova
|
||||
quasar -v // 检查 Quasar 版本
|
||||
python3 // 查看python版本
|
||||
cd /usr/src // 进入src
|
||||
wget https://www.python.org/ftp/python/3.9.5/Python-3.9.5.tgz // 下载python3.9.5
|
||||
tar -zxvf Python-3.9.5.tgz // 解压
|
||||
cd Python-3.9.5/ // 进入目录
|
||||
./configure --enable-optimizations // 编译
|
||||
make altinstall // 安装
|
||||
mv /usr/bin/python3 /usr/bin/python3.bak // 备份
|
||||
ln -s /usr/local/bin/python3.9 /usr/bin/python3 // 建立软连接
|
||||
mv /usr/bin/pip3 /usr/bin/pip3.bak // 备份
|
||||
ln -s /usr/local/bin/pip3.9 /usr/bin/pip3 // 建立软连接
|
||||
python3 // 查看python版本
|
||||
pip3 list // 查看pip3 是否安装成功
|
||||
yum install git // 安装git
|
||||
git clone https://github.com/Singosgu/GreaterWMS.git // 下载 GreaterWMS 从 github
|
||||
chmod -R 755 GreaterWMS // 提权 GreaterWMS 文件夹
|
||||
cd GreaterWMS // 进入GreaterWMS文件夹
|
||||
pip3 install -r requirements.txt
|
||||
// 有些时候,你安装这些库会出问题,是因为python3版本的问题,不用担心,pip3 install 出错的库就可以了.
|
||||
/usr/local/bin/daphne -p 8008 greaterwms.asgi:application
|
||||
// 现在打开浏览器,输入"127.0.0.1:8008",你会看到500错误,恭喜你,你已经可以正常部署接下来的事情了
|
||||
Ctrl + C // 回到GreaterWMS文件夹
|
||||
python3 manage.py makemigrations // 数据库生成
|
||||
python3 manage.py migrate // 数据库迁移
|
||||
/usr/local/bin/daphne -p 8008 greaterwms.asgi:application
|
||||
// 现在打开浏览器,输入"127.0.0.1:8008",你会看到项目已经运行了
|
||||
// 输入 "127.0.0.1:8008/myip", 你会得到你的内网IP,一定记住它
|
||||
Ctrl + C // 回到GreaterWMS文件夹
|
||||
cd templates //进入 templates 文件夹
|
||||
/usr/local/bin/yarn install // 等待Yarn安装完成,其实你也可以npm install ,就是会慢一点
|
||||
yarn config set registry https://registry.npm.taobao.org/ //更改yarn为国内源
|
||||
/usr/local/bin/quasar d // 使用quasar命令启动前端页面
|
||||
// 前端会向 "127.0.0.1:8008"发请求, 在这里我们只是看下项目是不是可以运行
|
||||
Ctrl + C // 退回到templates文件夹
|
||||
|
||||
从2.0.19版本以后,优化了请求地址修改方式,直接修改templates/dist/spa/statics/baseurl.js,中的baseurl和wsurl,就可以成功更改前端请求地址,不再需要做下面的quasar build打包工作。
|
||||
|
||||
如果需要修改前端内容,则还需要修改templates/public/statics/baseurl.js中的baseurl和wsurl,然后重新使用quasar build进行打包
|
||||
|
||||
cd src/boot // 进入在src/boot文件夹
|
||||
vim axios_request.js // 我们开始更改请求地址
|
||||
// 更改 "127.0.0.1" 成你的内网IP, baseurl 是http请求地址 , ws 是 websocket请求地址
|
||||
按下 Esc 然后输入 ":wq" 去保存修改
|
||||
// 现在,你已经知道怎么部署和修改请求地址了
|
||||
/usr/local/bin/quasar build // 需要对修改进行重新打包
|
||||
cd .. // 回到GreaterWMS文件夹
|
||||
/usr/local/bin/daphne -b 0.0.0.0 -p 8008 greaterwms.asgi:application
|
||||
// 现在,打开浏览器,输入 "你的内网IP:8008",你可以看到项目已经运行了
|
||||
|
||||
谢天谢地!!!
|
||||
|
||||
顺便说一句
|
||||
1. 你知道了怎么修改请求地址
|
||||
2. 你也可以按你的喜好,去更改Port
|
||||
3. 你更可以使用Nginx或者Apache,把项目发布到互联网上
|
||||
~~~
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
~~~shell
|
||||
su - // Enter root
|
||||
yum update
|
||||
yum upgrade
|
||||
yum -y install gcc-c++
|
||||
yum install zlib-devel xz-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel libffi-devel gcc make
|
||||
yum install nodejs
|
||||
npm install n -g
|
||||
/usr/local/bin/n 14.18.3 // update nodejs
|
||||
npm install npm -g
|
||||
npm install yarn -g
|
||||
npm install @quasar/cli -g
|
||||
npm install cordova -g
|
||||
cd /usr/src
|
||||
wget https://www.python.org/ftp/python/3.9.5/Python-3.9.5.tgz // download python3.9.5
|
||||
tar -zxvf Python-3.9.5.tgz
|
||||
cd Python-3.9.5/
|
||||
./configure --enable-optimizations
|
||||
make altinstall
|
||||
mv /usr/bin/python3 /usr/bin/python3.bak // backup python3
|
||||
ln -s /usr/local/bin/python3.9 /usr/bin/python3
|
||||
mv /usr/bin/pip3 /usr/bin/pip3.bak // backup pip3
|
||||
ln -s /usr/local/bin/pip3.9 /usr/bin/pip3
|
||||
yum install git // install git
|
||||
git clone https://github.com/Singosgu/GreaterWMS.git // clone GreaterWMS from github
|
||||
chmod -R 755 GreaterWMS
|
||||
cd GreaterWMS
|
||||
pip3 install -r requirements.txt
|
||||
python3 manage.py makemigrations
|
||||
python3 manage.py migrate
|
||||
/usr/local/bin/daphne -p 8008 greaterwms.asgi:application
|
||||
// open Chrom "127.0.0.1:8008/myip", you will get your internal lan ip, remember it
|
||||
Ctrl + C // back GreaterWMS folder
|
||||
cd templates // enter templates folder
|
||||
/usr/local/bin/yarn install
|
||||
/usr/local/bin/quasar d // it will start web site
|
||||
Ctrl + C // back templates folder
|
||||
cd src/boot
|
||||
vim axios_request.js // change request baseurl
|
||||
// change "127.0.0.1" to your internal IP, baseurl is for http, ws is for websocket
|
||||
// save it
|
||||
/usr/local/bin/quasar build // build your web site
|
||||
cd .. // back to GreaterWMS folder
|
||||
/usr/local/bin/daphne -b 0.0.0.0 -p 8008 greaterwms.asgi:application
|
||||
// now,you can use "internal IP:8008" to use greaterwms
|
||||
@@ -1 +0,0 @@
|
||||
22a265356ea69c3d30d96cfaaa591988adcc37b1
|
||||
@@ -1 +0,0 @@
|
||||
e1c14c564fee6800193b329912b295539a0f39ce
|
||||
@@ -1,202 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
https://www.56yhz.com
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1 +0,0 @@
|
||||
3ea6e343625852897cca8991854acf08d3f46700
|
||||
@@ -1,337 +0,0 @@
|
||||
# GreaterWMS--Open Source Warehouse Management System
|
||||
|
||||
<p><div align=center><img width="150" height="150" src="https://github.com/Singosgu/GreaterWMS/blob/master/static/img/logo.png?raw=true?raw=true"/></div></p>
|
||||
|
||||
---
|
||||
|
||||
[中文文档](https://github.com/Singosgu/GreaterWMS/blob/master/README_zh_Hans.md)
|
||||
|
||||
## Production Introduce:
|
||||
|
||||
Fully open source warehouse management system follows Apache License 2.0 and front-back stage decoupling method. API uses restful protocol to facilitate for add-on functions development. The html & js code is constructed with quasar(base on Vue.js v2.6.0+). According to API, it can support business models such as multi warehouse, wave shipment, combined picking and milk-run and so on.
|
||||
|
||||
- Software Copyright Procedures: 10136559
|
||||
- GitHub Link: [GitHub](https://github.com/Singosgu/GreaterWMS)
|
||||
- Gitee Link: [Gitee](https://gitee.com/Singosgu/GreaterWMS)
|
||||
- Video Tutorials: [YouTube](https://www.youtube.com/channel/UCPW1wciGMIEh7CYOdLnsloA)(All Tutorials Will Upload To Here)
|
||||
- Home Page: [DEMO](https://www.56yhz.com/)(Register can initial the demo data)
|
||||
- Mail:mail@56yhz.com
|
||||
- CIMO-ADMIN(vue-quasar-manage): [GitHub](https://github.com/972784674t/vue-quasar-manage) | [Gitee](https://gitee.com/incimo/vue-quasar-manage)
|
||||
|
||||
---
|
||||
|
||||
## Our Original Intention:
|
||||
|
||||
I have 15 years old experience focus on supply chain . I find that in this professional field . No freedom customize software can support our business deeply . Any software are closed-source and hardly to customize or dynamic with our suppliers & customers . So I design GreaterWMS , in order to give business highest freedom way to support trade development .
|
||||
|
||||
- Our Vision: If you work in a non IT industry and you love your industry, please using technology to change it.
|
||||
|
||||
---
|
||||
|
||||
## Development Environment:
|
||||
|
||||
- Python Version 3.9.5 +
|
||||
|
||||
- Django Version 3.1.12 +(This version of Django only supports asynchronous real-time communication)
|
||||
|
||||
- Django-rest-framework Version 3.12.2 + (Highest versions of Django-rest-framework are more compatible with Django3)
|
||||
|
||||
- Django-silk Version 4.1.0 (If you are deploying production, please turn off silk, which is only used for debugging API interface speed, which may leak users' information)
|
||||
|
||||
- Quasar Version 1.7.2 + (You can view the official website of quasar to edit the webside code of greater WMS: [Quasar](https://quasar.dev/))
|
||||
|
||||
- Vue Version 2.6.0 +(Try not to use vue3, because the development environment does not use vue3, I don't know what will happened)
|
||||
- API,Follow RESTful
|
||||
|
||||
---
|
||||
|
||||
## Build Command:
|
||||
|
||||
- Git Clone:
|
||||
|
||||
~~~shell
|
||||
git clone https://github.com/Singosgu/GreaterWMS.git
|
||||
~~~
|
||||
|
||||
- Install Python Library:
|
||||
|
||||
~~~python
|
||||
pip install -r requirements.txt
|
||||
~~~
|
||||
|
||||
Atention: `Installation requires Twisted library, this library sometimes cannot be installed, you need to download it and install it locally`
|
||||
|
||||
- Download Link:[TWISTED](https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted)
|
||||
|
||||
~~~python
|
||||
pip install Twisted{Version Name Which You Download}
|
||||
~~~
|
||||
|
||||
Atention: `Local installation needs to pay attention to the path`
|
||||
|
||||
- Makemigrations the Database:
|
||||
|
||||
~~~python
|
||||
python manage.py makemigrations
|
||||
~~~
|
||||
|
||||
- Migrate Database:
|
||||
|
||||
~~~python
|
||||
python manage.py migrate
|
||||
~~~
|
||||
|
||||
Create a database. Django uses sqlite3 as the default database. If you need other database, please configure DATABASE in greaterwms/settings.py
|
||||
|
||||
### Run Development Server:
|
||||
|
||||
- Dev Run:
|
||||
|
||||
~~~python
|
||||
daphne -p 8008 greaterwms.asgi:application
|
||||
~~~
|
||||
|
||||
### Run Production Server:
|
||||
|
||||
- supervisor Daemon:
|
||||
|
||||
~~~shell
|
||||
pip install supervisor
|
||||
~~~
|
||||
|
||||
Use supervisor to guard the Django process, and then use Nginx as a reverse proxy. As for supervisors, there are many tutorials, so I won’t explain them here.
|
||||
|
||||
- Nginx Support:
|
||||
|
||||
It is recommended to use Nginx for deployment. You need to specify the WebSocket link when deploying. If you do not specify it, the real-time communication function will report an error.
|
||||
|
||||
Also need to modify ws_url in templates/dist/spa/statics/baseurl.js
|
||||
|
||||
~~~shell
|
||||
## Example changes before
|
||||
const baseurl = 'http://127.0.0.1:8008/'
|
||||
const wsurl = 'ws://127.0.0.1:8008/'
|
||||
|
||||
## Example changes after
|
||||
const baseurl = 'https://Your Production Server/'
|
||||
const wsurl = 'wss://Your Production Server/websocket/'
|
||||
~~~
|
||||
|
||||
If the server has SSL enabled, please use https and wss, if SSL is not enabled, use http and ws
|
||||
|
||||
The front-end code needs to be rebuilt after modification
|
||||
|
||||
---
|
||||
|
||||
## Development Guide:
|
||||
|
||||
### Baseurl
|
||||
|
||||
- It is the basic URL for initiating the request. If it is for local debugging, the default is http://127.0.0.1:8008/. If it is deployed on the server, you need to change it to your website access URL
|
||||
|
||||
- The modification method is to modify axios_request.js. Attention: `The modification of websocket has been mentioned `
|
||||
|
||||
### Django-Silk
|
||||
|
||||
- Django-Silk is a debugging tool during development. It can count the response speed of each interface. If you need to deploy to a production environment, please delete the Django-silk related configuration, because there is a risk of leaking user information, or directly modify the Django-silk library , So that users can only see their request data
|
||||
|
||||
### Database Storage
|
||||
|
||||
- Data migration and other issues are considered during database design, so only the user_id in users and the user_id that comes with Django are foreign keys, and all the other fields do not use foreign keys, which is convenient for data backup and database migration.
|
||||
- The database is a 4-stage design
|
||||
|
||||
1. Verify data user ownership
|
||||
2. Verify data security
|
||||
3. Verify that the data can be stored in the database
|
||||
4. Save it in the database and return Response
|
||||
|
||||
### About Data Request
|
||||
|
||||
- The token value needs to be added to all request headers. This value is the unique identifier OPENID of the user’s data
|
||||
- All data transmission needs to set content-type to application/json
|
||||
|
||||
### OPENID
|
||||
|
||||
- OPENID is the unique identifier of registered user data. When the administrator registers directly, there will be developer=1 as the administrator ID.
|
||||
- You can do custom secondary development based on the developer label
|
||||
|
||||
### APPID
|
||||
|
||||
- APPID is the unique identifier of the user data group
|
||||
- If you need multi company operation or multi warehouse operation, you can make unified link through appid to realize multi company and multi warehouse operation
|
||||
|
||||
### User Jurisdiction
|
||||
|
||||
- There are not too many restrictions on user authorization. Please limit the secondary development according to your own business needs
|
||||
|
||||
---
|
||||
|
||||
## Business Process:
|
||||
|
||||
### Administrator
|
||||
|
||||
- Click Register, you can register as an administrator account, so as to realize the initialization program settings
|
||||
- After registration, you will get two IDs and one developer label. openid is the unique ID of the user data group. All data under this openid is bound through openid. Appid is the unique ID of the user group data. Multi company and multi warehouse functions are realized through appid. The developer ID is a Boolean value, and true represents an administrator account
|
||||
- There are two way for user login:
|
||||
1. Login directly with openid and staff name
|
||||
2. Administrator login with account and password
|
||||
- After login, the web will localstorage login information
|
||||
- you can view the openid of user data group by view my openid
|
||||
- If multi company and multi warehouse operation is required, pay attention to change openid
|
||||
- More administrator authrization, please develop by yourself
|
||||
|
||||
### Staff
|
||||
|
||||
- After registering as an administrator, create a new staff first
|
||||
- Staff has two fields, staff_ Name (for employee login), staff_ Type (employee type to control employee permissions)
|
||||
- The system does not have any restrictions on employee permissions. If you need employee permissions, please modify them according to the enterprise business model Templates
|
||||
- Click Edit to modify the employee information
|
||||
- Click Delete to delete the staff information. The system will set is_delete to true
|
||||
- Click Contact:
|
||||
1. You can directly chat with employees in real time, but you can't chat with yourself
|
||||
2. You can create a new notebook employee, which is actually used as a notebook
|
||||
3. In the personal center, you can view recent contacts
|
||||
4. The message flag will remind you how many unread messages you have
|
||||
|
||||
### Driver
|
||||
|
||||
- Driver management is only used in the shipping process
|
||||
- You need to know which driver picked up the goods
|
||||
|
||||
### Warehouse Set
|
||||
|
||||
- Warehouse
|
||||
1. You can create only one warehouse. Now you can create multiple warehouses, but only the first one will work
|
||||
2. If multi warehouse processing is needed, secondary development can be carried out through appid, or an administrator account can be created directly
|
||||
3. The city of the warehouse must be filled in, which is used to calculate the freight
|
||||
- Bin_Property
|
||||
1. The bin property determines the property of the goods in the warehouse
|
||||
2. Four property: damage, holding, inspection, normal
|
||||
3. In the beta version, property can be modified and deleted, but the release version can not
|
||||
4. All shipping process will only match the goods in the normal bin
|
||||
5. When goods are received and moved to another bin, the inventory quantity will be directly modified according to the bin property, and the inventory quantity of the warehouse will not be negative
|
||||
- Bin_Size
|
||||
1. The size of bin is to help the operator to check whether the goods can be put into the bin
|
||||
2. The current version does not check the dimensions of bin, and automatic inspection will be added in the future
|
||||
- Bin_Set
|
||||
1. Bin setting is necessary. Generally, bin setting is horizontal, vertical and horizontal, such as A010101, i.e. A horizontal, 01 vertical, 01 horizontal and 01 vertical
|
||||
2. The setting of bin needs to set the bin property and size. The property is very important, which determines whether the goods in this bin are normal goods
|
||||
|
||||
### Basic Info
|
||||
|
||||
- Company
|
||||
1. The creation of basic company information can only create one company. Now you can create multiple companies, but only the first one will work
|
||||
2. If multi company processing is needed, secondary development can be carried out through appid, or an administrator account can be created directly
|
||||
3. The city of the company must be filled in, which is used to show on the receipt-proof
|
||||
- Supplier
|
||||
1. Basic information of suppliers
|
||||
2. The city of the supplier must be filled in, which is used to display on the receipt-proof, and also to calculate the freight automatically
|
||||
- Customer
|
||||
1. Basic information of customers
|
||||
2. The customer's city must be filled in, which is used to display on the invoice, and also to automatically calculate the freight
|
||||
|
||||
### Godds
|
||||
|
||||
- Unit
|
||||
1. Goods units, the system will initialize to create some, but you can add and modify
|
||||
- Class
|
||||
1. Goods Class, you can add and modify
|
||||
- Color
|
||||
1. Goods color, the system will initialize to create some, but you can add and modify
|
||||
- Brand
|
||||
1. Goods brand, you can add and modify
|
||||
- Shape
|
||||
1. Goods Shape, the system will initialize to create some, but you can add and modify
|
||||
- Specs
|
||||
1. Goods specs, you can add and modify
|
||||
- Origin
|
||||
1. Goods Origin, where initial product goods, you can add and modify
|
||||
- Goods List
|
||||
1. Goods list
|
||||
|
||||
### Capital
|
||||
|
||||
- Capital
|
||||
1. The creation of fixed assets, not too much expansion, just record the use
|
||||
2. Can be statistical pallets accounts
|
||||
|
||||
### Stock Management
|
||||
|
||||
- Stock List
|
||||
1. Total inventory data of goods
|
||||
2. Onhand_ Stock quantity on hand
|
||||
3. Can order, which can be used to determine the inventory quantity of an order. Some goods have been ordered, but they can't be ordered any more even though they have existing inventory
|
||||
4. Ordered stock, the quantity of goods be ordered
|
||||
5. ASN stock has issued the arrival notice, but has not confirmed the quantity of goods in the arrival notice
|
||||
6. DN stock, has been ordered, but the order quantity has not been confirmed
|
||||
7. Pre Load,expected quantity of goods delivered
|
||||
8. Pre Sort,the quantity of goods that have arrived, unloaded and waiting to be sorted
|
||||
9. Sorted Stock, The quantity of goods waiting to be put on the bin after sorting
|
||||
10. Pick Stock, The picking list is generated from the delivery document, and the quantity of goods waiting to be picked
|
||||
11. Picked Stock, The quantity of goods that have been picked up and waiting to be handed over to the driver
|
||||
12. Back Order Stock, Order quantity in arrears
|
||||
- Bin Stock
|
||||
1. Total Stock, all inventory quantities of the product in this bin
|
||||
2. Pick Stock, the quantity of goods to be picked in this bin
|
||||
3. Picked stock, the quantity of goods picked in this bin
|
||||
4. Move to Bin: after moving, the inventory quantity will be updated directly according to the bin property. If all goods has been moved out from this bin, the bin will be updated to empty
|
||||
- Empty Bin
|
||||
1. Empty bin List
|
||||
- Occupied Bin
|
||||
1. Occupied bin list
|
||||
|
||||
### Inbound
|
||||
|
||||
- ASN
|
||||
1. ASN status = 1, the ASN arrival notice is created, and status 1 is the only status that can delete and modify the ASN information. It will be displayed in pre delivery, that is, there is an arrival notice, but it has not arrived. Click confirm delivery to confirm that the goods have arrived, and ASN status will be updated to 2. At this time, the ASN information can no longer be modified
|
||||
2. ASN status = 2, it is developed to queue up for the arrival of drivers. If we have many drivers arriving, it can be made into a queuing system. At the same time, it can also let the purchase and sales see the arrival information, reduce unnecessary email and telephone communication. Click finish loading to confirm that the goods have been unloaded, and Asn status will be updated to 3, The goods information will appear in sorting, and the ASN status indicates that the goods have been unloaded to the warehouse and are waiting for sorting
|
||||
3. ASN status = 3, goods sorting is a necessary process. Without goods sorting, goods cannot be put on shelves. The principle of putting on shelves is to arrange the goods and put them on the corresponding warehouse location. Click confirm sorted, and Asn status will be updated to 4, that is, confirm the sorting and wait for loading
|
||||
4. At this time, when you move the sorted page, the goods details that need to be put on the shelves will appear. Click move to bin to finish the listing. Of course, the system will automatically update the inventory quantity information of goods according to the location attributes after the listing
|
||||
|
||||
### Outbound
|
||||
|
||||
- DN
|
||||
1. DN status = 1, when the DN shipping order is created, the order status can still be modified, and the inventory quantity in the system will not change. Click confirm order, and the DN status will be updated to 2, that is, the order has been confirmed and cannot be changed. At the same time, the inventory quantity in the system will be automatically updated, such as can order quantity and ordered quantity
|
||||
2. DN Status = 2, This is the process when an order is confirmed and waiting to generate a picking list. You can click order release of a single order to generate a picking list of an order. You can also click release all order to generate a picking list of all orders. If all orders are released, the inventory will be matched according to the time sequence. When the inventory is insufficient, back will be generated In this process, the DN order number will change. For example, multiple orders of a customer will be unified into one order for picking. If the customer's order cannot be satisfied, the unsatisfied part will be generated as a shortage order. If the shortage order is not met by the matched inventory, no new order will be generated. Dn The status will be updated to 3, that is, in the process of waiting for picking, the status will be 2 when both the confirmed order and the shortage order are in the same status
|
||||
3. DN Status = 3,Direct picking, this function will appear in beta5 update
|
||||
4. DN Status = 4, Delivery handover, this function will appear in beta6 update
|
||||
5. DN Status = 5, Customer receiving, this function will appear in beta7 update
|
||||
6. DN Status = 6, When the reconciliation is finished and the order is closed, this function will appear in the beta7 update
|
||||
|
||||
### Reject Order
|
||||
|
||||
- RO
|
||||
This feature will add in the release version
|
||||
|
||||
### Screen Shot
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/inbound.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/outbound.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/stock.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/finace.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/goods.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/baseinfo.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/warehouse.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/staff.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/driver.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/api.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/chat.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img src="https://github.com/Singosgu/picfile/blob/master/CN/app1.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img src="https://github.com/Singosgu/picfile/blob/master/CN/app2.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img src="https://github.com/Singosgu/picfile/blob/master/CN/app3.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img src="https://github.com/Singosgu/picfile/blob/master/CN/app4.png?raw=true"/></div></p>
|
||||
|
||||
@@ -1,395 +0,0 @@
|
||||
# 聚商汇WMS--开源仓库管理系统
|
||||
|
||||
<p><div align=center><img width="150" height="150" src="https://github.com/Singosgu/GreaterWMS/blob/master/static/img/logo.png?raw=true?raw=true"/></div></p>
|
||||
|
||||
---
|
||||
|
||||
## 项目介绍:
|
||||
|
||||
完全开源仓储管理软件,遵循Apache License 2.0协议,前后端分离,且完全开源,API使用restful协议,方便二次开发,前端代码使用quasar进行构建,后端使用Python Django3.1,利用API,可以支持多仓,波次发货,合并拣货,Milk-Run等业务模型。
|
||||
|
||||
- 软件著作权编号:2018SR517685
|
||||
- GitHub地址:[GitHub](https://github.com/Singosgu/GreaterWMS)
|
||||
- Gitee地址:[Gitee](https://gitee.com/Singosgu/GreaterWMS)
|
||||
- 视频教程:[B站](https://space.bilibili.com/407321291/channel/detail?cid=152043)(所有的教程都会更新在这里)
|
||||
- Demo地址:[DEMO](https://www.56yhz.com/)(注册会获得初始化Demo数据)
|
||||
- 技术交流群:
|
||||
- 邮箱:mail@56yhz.com
|
||||
- CIMO-ADMIN(vue-quasar-manage): [GitHub](https://github.com/972784674t/vue-quasar-manage) | [Gitee](https://gitee.com/incimo/vue-quasar-manage)
|
||||
|
||||
---
|
||||
|
||||
## 项目初衷:
|
||||
|
||||
我在供应链行业工作了15年,发现在我们这个专业的领域,没有一款高自由度、高自定义化的软件,来深度支持我们企业的业务。大多数软件都是闭源的,而且很难去做二次开发,即使开发,周期也是非常长,开发失败的案例也是比比皆是。由于企业选择了一款软件后,其二次开发也会被开发公司绑定,至于二次开发费用,只能说呵呵。所以,我设计了这个聚商汇WMS,为的是做到一款高自由度,高自定义开发的仓库管理软件,来深度支持企业的业务。
|
||||
|
||||
- 愿景:如果你从事着非IT行业的工作,而你又热爱你的行业,那就用科技去改变他。
|
||||
|
||||
---
|
||||
|
||||
## 生命周期
|
||||
|
||||
- V 1.0.0 -- 2019年7月 ~ 2020年12月(由于1.0.0版本的二次开发设计较为复杂,故2.0重新编写)
|
||||
- V 2.0.0 -- 2020年12月 ~ 2021年3月(重新编写业务逻辑,原生自带API开发文档,加入实时通信,方便企业用户互相沟通)
|
||||
- V 2.1.0 -- 2021年3月 ~ 2021年6月(加入了客户与企业之间的实时互动,增进企业与客户之间的业务联系,实现VMI)
|
||||
- V 2.2.0 -- 2020年6月 ~ 2021年9月(加入了供应商与企业之间的实时互动,增进企业与供应商之间的业务联系,实现Milk-Run和看板拉动)
|
||||
- V 2.3.0 -- 2021年9月 ~ 2021年12月(库存管理雏形,初步加入神经网络,深度学习库存变化)
|
||||
- V 3.0.0 -- 2021年12月 ~ 2022年3月(完全植入神经网络,让上下游企业可以以最低的成本运营整体的业务)
|
||||
- V 3.1.0 -- 2022年3月 ~ 2022年6月(区域仓库业务布局,通过深度学习,实现多仓运营,成本最低化)
|
||||
|
||||
---
|
||||
|
||||
## 开发环境:
|
||||
|
||||
- Python 版本为 V 3.8.0 +
|
||||
|
||||
- Django 版本为 V 3.1.0 +(该版本Django才原生支持异步实时通信)
|
||||
|
||||
- Django-rest-framework 版本为 V 3.12.2 + (更高版本的Django-rest-Framework对Django3的兼容比较好)
|
||||
|
||||
- Django-silk 版本为 V 4.1.0 (如果是部署上线,请关闭silk,silk仅为调试API接口速度用,有可能会泄露用户信息)
|
||||
|
||||
- Quasar 版本为 V1.7.2 + (可以查看Quasar官网,来编辑GreaterWMS前端代码:[Quasar官网](http://www.quasarchs.com/))
|
||||
|
||||
- Vue 版本为 V 2.6.0 +(尽量不要使用Vue3,因为开发环境没有使用Vue3,不知道会出现什么问题)
|
||||
- API,遵循 RESTful 架构
|
||||
|
||||
---
|
||||
|
||||
## 构建命令:
|
||||
|
||||
- 下载代码:
|
||||
|
||||
~~~shell
|
||||
git clone https://github.com/Singosgu/GreaterWMS.git
|
||||
~~~
|
||||
|
||||
- 安装Python库:
|
||||
|
||||
~~~python
|
||||
pip install -r requirements.txt
|
||||
~~~
|
||||
|
||||
注意:`安装需要Twisted库,这个库有时候会安装不上,需要下载下来本地安装`
|
||||
|
||||
- 下载地址:[TWISTED](https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted)
|
||||
|
||||
~~~python
|
||||
pip install Twisted{你下载下来的版本名称}
|
||||
~~~
|
||||
|
||||
注意:`本地安装需要注意路径`
|
||||
|
||||
- 初始化数据库:
|
||||
|
||||
~~~python
|
||||
python manage.py makemigrations
|
||||
~~~
|
||||
|
||||
- 迁移数据库:
|
||||
|
||||
~~~python
|
||||
python manage.py migrate
|
||||
~~~
|
||||
|
||||
创建数据库,Django默认使用sqlite3作为数据库,如果需要mysql数据库,请在greaterwms/settings.py里面配置DATABASE
|
||||
|
||||
### 开发服务器运行:
|
||||
|
||||
- 开发运行:
|
||||
|
||||
~~~python
|
||||
daphne -p 8008 greaterwms.asgi:application
|
||||
~~~
|
||||
|
||||
### 生产服务器运行:
|
||||
|
||||
- supervisor守护进程:
|
||||
|
||||
~~~shell
|
||||
pip install supervisor
|
||||
~~~
|
||||
|
||||
使用supervisor来守护Django进程,再使用Nginx做反向代理,至于superevisor的教程有很多,这里不做讲解
|
||||
|
||||
- Nginx支持:
|
||||
|
||||
推荐使用Nginx进行部署,部署的时候需要指定WebSocket链接,如果不指定,实时通信功能将报错
|
||||
|
||||
另需要修改从2.0.19版本以后,优化了请求地址修改方式,直接修改templates/dist/spa/statics/baseurl.js,中的baseurl和wsurl,就可以成功更改前端请求地址,不再需要做下面的quasar build打包工作。
|
||||
|
||||
如果需要修改前端内容,则还需要修改templates/public/statics/baseurl.js中的baseurl和wsurl,然后重新使用quasar build进行打包里的ws_url
|
||||
|
||||
~~~shell
|
||||
## 示例更改前
|
||||
const baseurl = 'http://127.0.0.1:8008/'
|
||||
const wsurl = 'ws://127.0.0.1:8008/'
|
||||
|
||||
## 示例更改后
|
||||
const baseurl = 'https://你的域名/'
|
||||
const wsurl = 'wss://你的域名/websocket/'
|
||||
~~~
|
||||
|
||||
如果服务器启用了SSL,请使用https和wss,如果没有启用SSL,则使用http和ws
|
||||
|
||||
修改后需要重新build前端代码
|
||||
|
||||
---
|
||||
|
||||
## 开发扩展:
|
||||
|
||||
因为使用的前后端分离的设计,所以可以通过API,开发更多的软件应用
|
||||
|
||||
### 物流智能AGV
|
||||
|
||||
- AGV的项目也已经开源,由于场地受限,仅实现智能发货,定点回库,使用的循迹感应器,超声波避障感应器,红外避障感应器,所有的指令通过网络传输,AGV绑定MAC地址和IP地址,保证了安全性,前提是,你需要有一个树莓派。
|
||||
|
||||
### 进销存
|
||||
|
||||
- 可以直接当一个进销存系统使用,简化仓库库位设置等操作即可。
|
||||
|
||||
### APP和小程序
|
||||
|
||||
- Quasar原生可以直接打包成IOS APP和Android APP
|
||||
- 小程序的开发可以通过API开做二次开发,但小程序不支持put请求,所以需要自己再写一个请求接口。
|
||||
|
||||
- API的组合可以达到100万种,这样我们可以根据查询请求,来获得实时报表和数据监控
|
||||
|
||||
### 供应链管理系统
|
||||
|
||||
- 产品的数量,创建时间,最后使用时间是各方面统计的,所以可以方便采购计划和调拨计划进行库存的分析
|
||||
- V 2.3.0及其以后的版本,将自带深度学习分析,所以可以直接使用分析结果作为供应链管理系统工具使用
|
||||
|
||||
### 多仓管理
|
||||
|
||||
- OPENID为用户的数据唯一标识,数据组统一标识为APPID,所以很方便可以实现多仓管理
|
||||
|
||||
### 波次拣货,发货
|
||||
|
||||
- 可以设置固定时间向服务器发出请求,从而达到波次拣货的功能
|
||||
|
||||
- 也可以直接使用任务工作,通过API查询分析结果来实现,推荐使用[APScheduler](https://pypi.org/project/APScheduler/)
|
||||
|
||||
~~~python
|
||||
pip install apscheduler
|
||||
~~~
|
||||
|
||||
### Milk-Run
|
||||
|
||||
- V 2.2.0及其以上版本,将原生支持此功能
|
||||
- 如果现在就需要这个业务,可以根据API调用库存消耗,来实现此功能
|
||||
|
||||
### VMI
|
||||
|
||||
- V 2.1.0及其以上版本,将原生支持此功能
|
||||
- 如果现在就需要这个业务,可以根据API调用库存消耗,来实现此功能
|
||||
|
||||
### 拣货路线优化
|
||||
|
||||
- 现在的拣货路线是按照库位排序
|
||||
- V 2.3.0以后版本将原生支持此功能
|
||||
- 如果现在需要这个业务,可以根据每天的拣货明细,调用API来实现此功能
|
||||
|
||||
---
|
||||
|
||||
## 开发指南:
|
||||
|
||||
### baseurl
|
||||
|
||||
- 是发起请求的基本网址,如果是本地调试,则默认为http://127.0.0.1:8008/ ,如果部署在服务器,则需要将其改为你的网站访问url
|
||||
|
||||
- 修改方式为,修改axios_request.js,注意`websocket的修改之前已经提到了`
|
||||
|
||||
### Django-silk
|
||||
|
||||
- django-silk为开发时的调试工具,可以统计每个接口的响应速度,如果需要部署到生产环境,请删除Django-silk相关配置,因为会有泄露用户信息的风险,或者直接修改Django-silk库,让用户只能看到自己的请求数据
|
||||
|
||||
### 数据库存储
|
||||
|
||||
- 数据库设计时考虑到数据迁移等问题,所以只有users里面的user_id和Django自带的user_id做了外键,其余所有字段全部没有使用外键,方便数据备份和数据库迁移
|
||||
- 数据库是4段式设计
|
||||
1. 验证数据用户归属
|
||||
2. 验证数据安全性
|
||||
3. 验证数据是否可以存入数据库
|
||||
4. 存入数据库,并返回Response
|
||||
|
||||
### 关于数据传输
|
||||
|
||||
- 需要在所有的请求头headers里面加入token值,这个值就是用户的数据唯一标识OPENID
|
||||
- 所有的数据传输需要设定content-type为application/json
|
||||
|
||||
### OPENID
|
||||
|
||||
- OPENID是注册用户数据的唯一标识,当管理员直接注册时,会有developer=1这个管理员标识。
|
||||
- 你可以根据developer标识来做自定义二次开发
|
||||
|
||||
### APPID
|
||||
|
||||
- APPID是用户数据组唯一标识
|
||||
- 如果需要多公司运营,或者多仓运营,可以通过APPID做统一链接,来实现多公司,多仓操作
|
||||
|
||||
### 用户权限
|
||||
|
||||
- 未对用户权限做过多限制,请根据自身的业务需要,做二次开发限制
|
||||
|
||||
---
|
||||
|
||||
## 业务流程:
|
||||
|
||||
### 管理员
|
||||
|
||||
- 点击注册,可以注册成为管理员账号,从而实现初始化程序设置
|
||||
- 注册后会得到2个ID和1个开发者标识,OPENID是用户数据组唯一标识,通过OPENID绑定此OPENID下所有的数据,APPID是用户组数据唯一标识,通过APPID来实现多公司,多仓库功能,Developer标识是个布尔值,True代表这是个管理员账号
|
||||
- 用户登入分2种:
|
||||
1. 使用OPENID和员工名称直接登入
|
||||
2. 管理员使用账号和密码登入
|
||||
- 登入后前端会存储登入信息
|
||||
- 可以通过查看我的OPENID来查看用户数据组的OPENID
|
||||
- 如果需要多公司,多仓库操作,注意需要更改OPENID
|
||||
- 更多管理员权限,请自行开发
|
||||
|
||||
### 员工管理
|
||||
- 注册管理员后,新建一个员工
|
||||
- 员工有2个字段,Staff_name(用于员工登入),Staff_type(员工类型来控制员工的权限)
|
||||
- 系统没有对员工权限做任何限制,如果需要员工权限,请根据企业业务模型,自行修改Templates
|
||||
- 点击Edit,可以修改员工信息
|
||||
- 点击Delete,可以删除员工信息,系统后台会将Is_delete调成True
|
||||
- 点击Contact:
|
||||
1. 可以直接和员工实时聊天,但是不可以和自己聊天
|
||||
2. 可以新建一个备忘录员工,这样做其实是当成备忘录使用
|
||||
3. 在个人中心,可以查看最近的联系人
|
||||
4. Message标识会提醒你现在有多少未读消息
|
||||
|
||||
### 司机管理
|
||||
- 司机管理只会在发货流程中用到
|
||||
- 你需要知道货物是哪个司机提货取走的
|
||||
|
||||
### 仓库设置
|
||||
- Warehouse
|
||||
1. 仓库的创建只可以创建一个仓库,现在可以创建多个,但是只有第一个会起作用
|
||||
2. 如果需要多仓处理,可以通过APPID进行二次开发,也可以直接重新创建一个管理员账号
|
||||
3. 仓库的城市一定要填写,这是用来计算运费的
|
||||
- Bin_Property
|
||||
1. 库位属性决定了仓库中货物属于什么属性的货物
|
||||
2. 4种属性:破损(Damage),锁定(Holding),质检(Inspection),正常(Normal)
|
||||
3. Beta版中,属性可以修改和删除,正式版将无法删除和修改
|
||||
4. 所有的发货,都只会匹配Normal库位的货物
|
||||
5. 收货上架和移库,都会根据库位属性,直接修改库存数量,仓库的库存数量不会出现负数
|
||||
- Bin_Size
|
||||
1. 库位的尺寸是帮助操作人员查看货物是否可以放入库位
|
||||
2. 现行的版本没有对上架和移库尺寸做检查,将来会加入自动检查
|
||||
- Bin_Set
|
||||
1. 库位设置是必须的,通常库位设置是横纵横纵,比如A010101,即A横01纵01横01纵
|
||||
2. 库位的设置需要设置库位属性和尺寸,属性很重要,他决定了此库位的货物是否为正常货物
|
||||
|
||||
### 基础设置
|
||||
- Company
|
||||
1. 公司基本信息的创建只可以创建一个公司,现在可以创建多个,但是只有第一个会起作用
|
||||
2. 如果需要多公司处理,可以通过APPID进行二次开发,也可以直接重新创建一个管理员账号
|
||||
3. 公司的城市一定要填写,这是用来显示在收发货单上的
|
||||
- Supplier
|
||||
1. 供应商的基础信息
|
||||
2. 供应商的城市一定要填写,这是用来显示在收货单上的,并且也是要自动计算运费的
|
||||
- Customer
|
||||
1. 客户的基础信息
|
||||
2. 客户的城市一定要填写,这是用来显示在发货单上的,并且也是要自动计算运费的
|
||||
|
||||
### 商品管理
|
||||
- Unit
|
||||
1. 商品的单位,系统会初始化创建一些,但可以自己添加和修改
|
||||
- Class
|
||||
1. 商品的类型,可以自己添加和修改
|
||||
- Color
|
||||
1. 商品的颜色,系统会初始化创建一些,但可以自己添加和修改
|
||||
- Brand
|
||||
1. 商品的品牌,可以自己添加和修改
|
||||
- Shape
|
||||
1. 商品的形状,系统会初始化创建一些,但可以自己添加和修改
|
||||
- Specs
|
||||
1. 商品的规格,可以自己添加和修改
|
||||
- Origin
|
||||
1. 商品的产地,可以自己添加和修改
|
||||
- Goods List
|
||||
1. 商品的列表
|
||||
|
||||
### 固定资产
|
||||
- Capital
|
||||
1. 固定资产创建,没有做过多拓展,只是记录使用
|
||||
2. 可以统计托盘账目等
|
||||
|
||||
### 库存管理
|
||||
- Stock List
|
||||
1. 在库的货物总的库存数据量
|
||||
2. Onhand_stock现有的库存数量
|
||||
3. Can Order,可以用于下单发货的库存数量,因为有些货物已经被下了订单,虽然有现有库存,但是不可以再被订货
|
||||
4. Ordered Stock,已经被下单的货物数量
|
||||
5. ASN Stock,已经下了到货通知书,但还没有确认到货通知书的货物数量
|
||||
6. DN Stock,已被下单,但是还没有确认订单数量
|
||||
7. Pre Load,预计到货货物数量
|
||||
8. Pre Sort,已经到货,卸货完成,等待分拣的货物数量
|
||||
9. Sorted Stock,货物分拣完成,等待上架的货物数量
|
||||
10. Pick Stock,发货单生成了拣货单,等待拣货的货物数量
|
||||
11. Picked Stock,已经拣货完成,等待和司机交接的货物数量
|
||||
12. Back Order Stock,欠货订单数量
|
||||
- Bin Stock
|
||||
1. Total Stock,这个库位该产品的所有库存数量
|
||||
2. Pick Stock,这个库位需要拣货的数量
|
||||
3. Picked Stock,这个库位拣货完成的数量
|
||||
4. Move To Bin, 移库,移库后,会根据库位属性,直接更新库存数量,如果库位全部移空,则该库位会更新为空库位
|
||||
- Empty Bin
|
||||
1. 空库位明细
|
||||
- Occupied Bin
|
||||
1. 非空库位明细
|
||||
|
||||
### 收货管理
|
||||
- ASN到货通知书状态
|
||||
1. ASN Status = 1, ASN到货通知书创建完成,状态1是唯一可以删除和修改ASN信息的状态,他会显示在Pre Delivery中,即有了到货通知书,但是还没有到货,点击Confirm Delivery,即确认货物已经到达,ASN Status更新到2,此时已经无法再修改ASN信息
|
||||
2. ASN Status = 2, 拓展开发为司机到货排队,如果我们有很多司机到货,这可以做成一个排队系统,同时也可以让采购和销售看到到货信息,减少不必要的邮件和电话沟通,点击Finish Loading,即确认货物已经卸货完成,ASN Status更新到3,货物信息会出现在Sorting,此时的ASN状态表示,货物已卸到仓库,等待分拣
|
||||
3. ASN Status = 3, 货物分拣是必须的一个流程,没有货物分拣,货物是无法上架的,上架的原则就是货物整理好,摆放到相对应的库位上,点击Confirm Sorted,ASN Status更新到4,即确认分拣完成,等待上架
|
||||
4. 此时移动Sorted页面,会出现需要上架的货物明细,点击Move To Bin,上架完成,当然,系统会根据上架后的库位属性,自动更新商品库存数量信息
|
||||
### 发货管理
|
||||
- DN发货单状态
|
||||
1. DN Status = 1, DN发货单创建完成,此时订单还是可以修改状态,且系统中的库存数量不会发生任何改变,点击Confirm Order,DN Status更新到2,即订单已经被确认,且无法更改,同时系统中的货物库存数量会自动更新,比如Can Order数量和Ordered数量
|
||||
2. DN Status = 2, 这是订单被确认等待生成拣货单的过程,你可以点击单条订单Order Release来生成一个订单的拣货单,你也可以点击Release All Order,来将所有订单生成拣货单,如果是所有订单Release,那么会根据时间的先后进行库存匹配,库存不足时,会生成Back Order,即欠货订单,在这个过程中,DN单号是会发生改变的,如一家客户的多张订单,会被统一到一张订单中进行拣货,如客户订单无法满足,会将未满足部分生成欠货订单,欠货订单如果仍未得到匹配库存满足,将不再生成新的订单,DN Status会更新到3,即等待拣货的过程,已确认的订单和欠货订单都时Status为2的状态
|
||||
3. DN Status = 3, 直接拣货,此功能会出现在Beta5更新
|
||||
4. DN Status = 4, 发货交接,此功能会出现在Beta6更新
|
||||
5. DN Status = 5, 客户签收,此功能会出现在Beta7更新
|
||||
6. DN Status = 6, 对账结束,订单关闭,此功能会出现在Beta7更新
|
||||
### 退货管理
|
||||
- RO退货订单
|
||||
此功能将会出现在正式版中
|
||||
### 运费管理
|
||||
- Transportation Fee
|
||||
API已经完成,前端暂未更新入口,如果想要使用,可以直接调用Payment下的Transportation Fee API进行使用,运费自动计算模块已经做进收发货流程中
|
||||
|
||||
### 界面截图
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/inbound.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/outbound.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/stock.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/finace.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/goods.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/baseinfo.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/warehouse.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/staff.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/driver.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/api.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img width="100%" height="100%" src="https://github.com/Singosgu/picfile/blob/master/CN/chat.png?raw=true"/></div></p>
|
||||
<p><div align=center><img src="https://github.com/Singosgu/picfile/blob/master/CN/app1.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img src="https://github.com/Singosgu/picfile/blob/master/CN/app2.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img src="https://github.com/Singosgu/picfile/blob/master/CN/app3.png?raw=true"/></div></p>
|
||||
|
||||
<p><div align=center><img src="https://github.com/Singosgu/picfile/blob/master/CN/app4.png?raw=true"/></div></p>
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
7617270580ae099de75afba1ee0dfaf912f22a6d
|
||||
@@ -1,65 +0,0 @@
|
||||
~~~shell
|
||||
sudo apt update
|
||||
sudo apt upgrade
|
||||
sudo apt install vim-gtk // 安装 vim
|
||||
cd ~ // 到达Home目录
|
||||
vi .bashrc // 把 "alias vi=vim" 加进 bashrc
|
||||
source .bashrc // 刷新生效bashrc
|
||||
sudo apt install git // 安装 git
|
||||
sudo git clone https://github.com/Singosgu/GreaterWMS.git // 下载 GreaterWMS 从 github
|
||||
sudo apt install nodejs // 安装 nodejs
|
||||
sudo apt install npm // 安装 npm
|
||||
sudo npm install n -g // 安装 n
|
||||
sudo n stable // 下载 nodejs 的稳定版本
|
||||
// 你需要确定你的nodejs是12或者14版本,Quasar只支持12或者14版本
|
||||
// 这步完成以后,你需要重新启动你的Terminal,要不然升级不生效
|
||||
sudo npm install npm -g // 升级 NPM 到最新版本
|
||||
sudo npm install yarn -g // 安装 yarn
|
||||
sudo npm install -g @quasar/cli // 安装 quasar/cli
|
||||
quasar -v // 检查 Quasar 版本
|
||||
python3 // 确定你的python版本是3.8以上版本,原则上3.6也是可以的,但是安装库会有些问题
|
||||
pip3 list // 确定你是否安装有 pip3
|
||||
sudo apt install python3-pip // 如果你没有pip3 ,就安装一下
|
||||
pip3 list // 检查下是否安装成功
|
||||
sudo chmod -R 755 GreaterWMS // 提权 GreaterWMS 文件夹
|
||||
cd GreaterWMS // 进入GreaterWMS文件夹
|
||||
sudo pip3 install -r requirements.txt
|
||||
// 有些时候,你安装这些库会出问题,是因为python3版本的问题,不用担心,sudo pip3 install 出错的库就可以了.
|
||||
sudo daphne -p 8008 greaterwms.asgi:application
|
||||
// 现在打开浏览器,输入"127.0.0.1:8008",你会看到500错误,恭喜你,你已经可以正常部署接下来的事情了
|
||||
Ctrl + C // 回到GreaterWMS文件夹
|
||||
sudo python3 manage.py makemigrations // 数据库生成
|
||||
sudo python3 manage.py migrate // 数据库迁移
|
||||
sudo daphne -p 8008 greaterwms.asgi:application
|
||||
// 现在打开浏览器,输入"127.0.0.1:8008",你会看到项目已经运行了
|
||||
// 输入 "127.0.0.1:8008/myip", 你会得到你的内网IP,一定记住它
|
||||
Ctrl + C // 回到GreaterWMS文件夹
|
||||
cd templates //进入 templates 文件夹
|
||||
|
||||
从2.0.19版本以后,优化了请求地址修改方式,直接修改templates/dist/spa/statics/baseurl.js,中的baseurl和wsurl,就可以成功更改前端请求地址,不再需要做下面的quasar build打包工作。
|
||||
|
||||
如果需要修改前端内容,则还需要修改templates/public/statics/baseurl.js中的baseurl和wsurl,然后重新使用quasar build进行打包
|
||||
|
||||
sudo yarn config set registry https://registry.npm.taobao.org/ //更改yarn为国内源
|
||||
sudo yarn install // 等待Yarn安装完成,其实你也可以sudo npm install ,就是会慢一点
|
||||
sudo quasar d // 使用quasar命令启动前端页面
|
||||
// 前端会向 "127.0.0.1:8008"发请求, 在这里我们只是看下项目是不是可以运行
|
||||
Ctrl + C // 退回到templates文件夹
|
||||
cd src/boot // 进入在src/boot文件夹
|
||||
sudo vim axios_request.js // 我们开始更改请求地址
|
||||
// 更改 "127.0.0.1" 成你的内网IP, baseurl 是http请求地址 , ws 是 websocket请求地址
|
||||
按下 Esc 然后输入 ":wq" 去保存修改
|
||||
// 现在,你已经知道怎么部署和修改请求地址了
|
||||
sudo quasar build // 需要对修改进行重新打包
|
||||
cd .. // 回到GreaterWMS文件夹
|
||||
sudo daphne -b 0.0.0.0 -p 8008 greaterwms.asgi:application
|
||||
// 现在,打开浏览器,输入 "你的内网IP:8008",你可以看到项目已经运行了
|
||||
|
||||
谢天谢地!!!
|
||||
|
||||
顺便说一句
|
||||
1. 你知道了怎么修改请求地址
|
||||
2. 你也可以按你的喜好,去更改Port
|
||||
3. 你更可以使用Nginx或者Apache,把项目发布到互联网上
|
||||
~~~
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
~~~shell
|
||||
sudo apt update
|
||||
sudo apt upgrade
|
||||
sudo apt install vim-gtk // install vim
|
||||
cd ~ // Go to home follder
|
||||
vi .bashrc // add "alias vi=vim" in bashrc
|
||||
source .bashrc // refresh bashrc
|
||||
sudo apt install git // install git
|
||||
sudo git clone https://github.com/Singosgu/GreaterWMS.git // Download GreaterWMS from github
|
||||
sudo apt install nodejs // install nodejs
|
||||
sudo apt install npm // install npm
|
||||
sudo npm install n -g // update n
|
||||
sudo n stable // Download nodejs to stable version
|
||||
// You must confirm your nodejs version is 14 or 12 , cause Quasar just support 12 or 14
|
||||
// after this step , you should re-open your terminal
|
||||
sudo npm install npm -g // update npm to latestest
|
||||
sudo npm install yarn -g // install yarn
|
||||
sudo npm install -g @quasar/cli // install quasar/cli
|
||||
quasar -v // check quasar version
|
||||
python3 // To check your python version is 3.8 above
|
||||
pip3 list // To check whether you have pip3
|
||||
sudo apt install python3-pip // if you don't have pip3 , install it
|
||||
pip3 list // check again
|
||||
sudo chmod -R 755 GreaterWMS // approve GreaterWMS chmod
|
||||
cd GreaterWMS
|
||||
sudo pip3 install -r requirements.txt
|
||||
// Some times , you can not install some lib . Cause your python3 version . Don't worry, sudo pip3 install it is ok.
|
||||
sudo daphne -p 8008 greaterwms.asgi:application
|
||||
// Now, Open the brower and enter "127.0.0.1:8008", If you see 500 error . Congratuation, you success.
|
||||
Ctrl + C // out to terminal
|
||||
sudo python3 manage.py makemigrations // database create
|
||||
sudo python3 manage.py migrate // database create
|
||||
sudo daphne -p 8008 greaterwms.asgi:application
|
||||
// Now, Open the brower and enter "127.0.0.1:8008". You can see our project run
|
||||
// Now , Enter "127.0.0.1:8008/myip", you will get Intranet ip, recorde it
|
||||
Ctrl + C // out to terminal
|
||||
cd templates // Go to templates follder
|
||||
sudo yarn install // Waiting for yarn install
|
||||
sudo quasar d // run quasar web
|
||||
// quasar web will send request to "127.0.0.1:8008", this step just to check whether it can be run
|
||||
Ctrl + C // out to terminal
|
||||
cd src/boot // go to boot follder
|
||||
sudo vim axios_request.js // go to this file to change the request link
|
||||
// change "127.0.0.1" to your Intranet ip , baseurl is for http , ws is for websocket
|
||||
Esc then enter ":wq" to save the change
|
||||
// till now you know how to build it and change the request
|
||||
sudo quasar build // Re-Build the Quasar Web
|
||||
cd .. // back to greaterwms follder
|
||||
sudo daphne -b 0.0.0.0 -p 8008 greaterwms.asgi:application
|
||||
// Now, Open the brower and enter "your Intranet ip:8008". You can see our project run
|
||||
|
||||
Congratuation!!!
|
||||
|
||||
By The Way
|
||||
1. You Know how to change the request
|
||||
2. You also can change the port which you like
|
||||
3. You can use Nginx or Apache to run the project on internet
|
||||
~~~
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
d845ab52a97e28ec0282b25020c1284479f920f3
|
||||
@@ -1,105 +0,0 @@
|
||||
~~~shell
|
||||
# 下载python3.9.2(版本以自己电脑系统为主,我们以64位为例)
|
||||
https://www.python.org/ftp/python/3.9.2/python-3.9.2-amd64.exe
|
||||
# 右键,以管理员运行exe文件,安装python3.9.2
|
||||
# 注意一定要勾选上Add Python3.9 To PATH,然后点选Install Now
|
||||
# 下载sqlite3(版本以自己电脑系统为主,我们以64位为例)
|
||||
https://www.sqlite.org/2021/sqlite-dll-win64-x64-3350500.zip
|
||||
# 解压zip文件,将解压出来的文件,覆盖python路径dll中的文件,地址为
|
||||
~ C:\Users\{你的用户名}\AppData\Local\Programs\Python\Python39\DLLs
|
||||
# 下载Node.JS14.16.1(版本以自己电脑系统为主,我们以64位为例)
|
||||
https://nodejs.org/dist/v14.16.1/node-v14.16.1-x64.msi
|
||||
# 安装Node.JS的时候,一定不要勾选Automatically,一直下一步,知道安装完成
|
||||
# 下载Git(版本以自己电脑系统为主,我们以64位为例,需要下载setup版本)
|
||||
https://git-scm.com/download/win
|
||||
# 右键,以管理员运行exe文件,然后一直下一步就可以了
|
||||
# 选择好你要把GreaterWMS摆在哪个目录中,右键,选择Git Bash Here
|
||||
# 下载 GreaterWMS 从 github,由于网络原因,会很慢,多试几次
|
||||
git clone https://github.com/Singosgu/GreaterWMS.git
|
||||
# 左下角搜索栏,输入cmd
|
||||
# 右键,以管理员运行cmd
|
||||
# 查看Python版本
|
||||
python -V
|
||||
# 查看pip有没有装好
|
||||
pip list
|
||||
# 升级pip到最新版本
|
||||
pip install --upgrade pip
|
||||
# 进入GreaterWMS摆放目录,演示时摆在downlowad里面的,所以我们进去目录
|
||||
~ cd C:\Users\{你的用户名}\Downloads\GreaterWMS\
|
||||
# pip安装python依赖库
|
||||
pip install -r requirements.txt
|
||||
# Twisted可能安装不上,需要下载下来手动安装
|
||||
https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
|
||||
# 下载自己适合的版本,比如:我的演示视频是Python3.9.2,Win10版本是64位的
|
||||
# 所以我就要下载Twisted-20.3.0-cp39-cp39-win_amd64.whl
|
||||
# 将下载下来的Twisted摆在GreaterWMS根目录,手动安装
|
||||
pip install Twisted-20.3.0-cp39-cp39-win_amd64.whl
|
||||
# 再次运行安装requirements.txt
|
||||
pip install -r requirements.txt
|
||||
# 启动GreaterWMS
|
||||
daphne -p 8008 greaterwms.asgi:application
|
||||
# 这时候打开浏览器,输入127.0.0.1:8008
|
||||
# 如果看到500报错,就说明之前的Python依赖已经全部安装完成了
|
||||
# 回到CMD界面,按住Ctrl+C来退出项目启动
|
||||
# 生成数据库迁移文件
|
||||
python manage.py makemigrations
|
||||
# 生成数据库
|
||||
python manage.py migrate
|
||||
# 再次启动项目
|
||||
daphne -p 8008 greaterwms.asgi:application
|
||||
# 这时候打开浏览器,输入127.0.0.1:8008
|
||||
# 查看局域网IP,浏览器输入127.0.0.1:8008/myip
|
||||
# 保存或者记住这个IP地址
|
||||
# 一定注意,windows每次启动获得的内网IP是不同的,要么你路由器设置固定内网IP给这台电脑,要么你就不要关电脑
|
||||
# 回到CMD界面,按住Ctrl+C来退出项目启动
|
||||
# 进入templates目录
|
||||
cd templates
|
||||
|
||||
从2.0.19版本以后,优化了请求地址修改方式,直接修改templates/dist/spa/statics/baseurl.js,中的baseurl和wsurl,就可以成功更改前端请求地址,不再需要做下面的quasar build打包工作。
|
||||
|
||||
如果需要修改前端内容,则还需要修改templates/public/statics/baseurl.js中的baseurl和wsurl,然后重新使用quasar build进行打包
|
||||
|
||||
# 升级下npm
|
||||
npm install -g npm
|
||||
# 切换npm源为国内源
|
||||
npm config set registry https://registry.npm.taobao.org
|
||||
# 安装Yarn
|
||||
npm install -g yarn
|
||||
# 更改yarn为国内源
|
||||
yarn config set registry https://registry.npm.taobao.org/
|
||||
# 安装quasar环境
|
||||
npm install -g @quasar/cli
|
||||
# 安装windows构建工具
|
||||
#注意:如果安装不上请下载 Visual Studio 安装C++环境
|
||||
npm install -g windows-build-tools
|
||||
# 安装core-js依赖
|
||||
npm install -g core-js
|
||||
# 查看全局依赖是否安装完成
|
||||
npm list -g --depth=0
|
||||
# 安装项目依赖
|
||||
yarn install
|
||||
# 这个过程会有点慢,有时候会很快,是因为网络原因
|
||||
# 如果发生报错,那是因为网络原因无法安装,多试几次就可以了,直到没有报错
|
||||
# 进入发请求的文件,修改请求地址
|
||||
~ 记事本编辑 GreaterWMS/templates/src/boot/axios_request
|
||||
# 将127.0.0.1更改为你刚才查看到的内网IP
|
||||
const baseurl = 'http://127.0.0.1:8008/'
|
||||
const wsurl = 'ws://127.0.0.1:8008/'
|
||||
# 保存退出
|
||||
# templates目录下重新编译前端
|
||||
quasar build
|
||||
# 回到GreaterWMS根目录
|
||||
cd ..
|
||||
# 启动项目加入-b 0.0.0.0参数
|
||||
daphne -b 0.0.0.0 -p 8008 greaterwms.asgi:application
|
||||
# 接下来就可以使用你的浏览器,访问{ http://内网IP:8008 }来查看该项目了
|
||||
# 局域网上的电脑也可以通过这个IP来访问项目
|
||||
|
||||
谢天谢地!!!
|
||||
|
||||
顺便说一句
|
||||
1. 你知道了怎么修改请求地址
|
||||
2. 你也可以按你的喜好,去更改Port
|
||||
3. 你更可以使用Nginx或者Apache,把项目发布到互联网上
|
||||
~~~
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
63e42d89d6750efd58197b6b61d732d9f309ffd2
|
||||
@@ -1 +0,0 @@
|
||||
8b42c0ea8087964023ae7aa09fae24b32cc62b14
|
||||
@@ -1,5 +0,0 @@
|
||||
from django.contrib import admin
|
||||
from . models import AsnListModel, AsnDetailModel
|
||||
|
||||
admin.site.register(AsnListModel)
|
||||
admin.site.register(AsnDetailModel)
|
||||
@@ -1,5 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AsnConfig(AppConfig):
|
||||
name = 'asn'
|
||||
-116
@@ -1,116 +0,0 @@
|
||||
from rest_framework_csv.renderers import CSVStreamingRenderer
|
||||
|
||||
def list_file_headers():
|
||||
return [
|
||||
'asn_code',
|
||||
'asn_status',
|
||||
'total_weight',
|
||||
'total_volume',
|
||||
'total_cost',
|
||||
'supplier',
|
||||
'creater',
|
||||
'create_time',
|
||||
'update_time'
|
||||
]
|
||||
|
||||
def list_cn_data_header():
|
||||
return dict([
|
||||
('asn_code', u'ASN单号'),
|
||||
('asn_status', u'ASN状态'),
|
||||
('total_weight', u'总重量'),
|
||||
('total_volume', u'总体积'),
|
||||
('total_cost', u'总成本'),
|
||||
('supplier', u'供应商'),
|
||||
('creater', u'创建人'),
|
||||
('create_time', u'创建时间'),
|
||||
('update_time', u'更新时间')
|
||||
])
|
||||
|
||||
def list_en_data_header():
|
||||
return dict([
|
||||
('asn_code', u'ASN Code'),
|
||||
('asn_status', u'ASN Status'),
|
||||
('total_weight', u'Total Weight'),
|
||||
('total_volume', u'Total Volume'),
|
||||
('total_cost', u'Total Cost'),
|
||||
('supplier', u'Supplier'),
|
||||
('creater', u'Creater'),
|
||||
('create_time', u'Create Time'),
|
||||
('update_time', u'Update Time')
|
||||
])
|
||||
|
||||
def detail_file_headers():
|
||||
return [
|
||||
'asn_code',
|
||||
'asn_status',
|
||||
'supplier',
|
||||
'goods_code',
|
||||
'goods_qty',
|
||||
'goods_actual_qty',
|
||||
'sorted_qty',
|
||||
'goods_shortage_qty',
|
||||
'goods_more_qty',
|
||||
'goods_damage_qty',
|
||||
'goods_weight',
|
||||
'goods_volume',
|
||||
'goods_cost',
|
||||
'creater',
|
||||
'create_time',
|
||||
'update_time'
|
||||
]
|
||||
|
||||
def detail_cn_data_header():
|
||||
return dict([
|
||||
('asn_code', u'ASN单号'),
|
||||
('asn_status', u'ASN状态'),
|
||||
('supplier', u'供应商'),
|
||||
('goods_code', u'商品编码'),
|
||||
('goods_qty', u'订单数量'),
|
||||
('goods_actual_qty', u'实际到货数量'),
|
||||
('sorted_qty', u'已分拣数量'),
|
||||
('goods_shortage_qty', u'少到货数量'),
|
||||
('goods_more_qty', u'多到货数量'),
|
||||
('goods_damage_qty', u'破损数量'),
|
||||
('goods_weight', u'商品重量'),
|
||||
('goods_volume', u'商品体积'),
|
||||
('goods_cost', u'商品成本'),
|
||||
('creater', u'创建人'),
|
||||
('create_time', u'创建时间'),
|
||||
('update_time', u'更新时间')
|
||||
])
|
||||
|
||||
def detail_en_data_header():
|
||||
return dict([
|
||||
('asn_code', u'ASN Code'),
|
||||
('asn_status', u'ASN Status'),
|
||||
('supplier', u'Supplier'),
|
||||
('goods_code', u'Goods Code'),
|
||||
('goods_qty', u'Goods Qty'),
|
||||
('goods_actual_qty', u'Goods Actual Qty'),
|
||||
('sorted_qty', u'Sorted Qty'),
|
||||
('goods_shortage_qty', u'Goods Shortage Qty'),
|
||||
('goods_more_qty', u'Goods More Qty'),
|
||||
('goods_damage_qty', u'Goods Damage Qty'),
|
||||
('goods_weight', u'Goods Weight'),
|
||||
('goods_volume', u'Goods Volume'),
|
||||
('goods_cost', u'Goods Cost'),
|
||||
('creater', u'Creater'),
|
||||
('create_time', u'Create Time'),
|
||||
('update_time', u'Update Time')
|
||||
])
|
||||
|
||||
class FileListRenderCN(CSVStreamingRenderer):
|
||||
header = list_file_headers()
|
||||
labels = list_cn_data_header()
|
||||
|
||||
class FileListRenderEN(CSVStreamingRenderer):
|
||||
header = list_file_headers()
|
||||
labels = list_en_data_header()
|
||||
|
||||
class FileDetailRenderCN(CSVStreamingRenderer):
|
||||
header = detail_file_headers()
|
||||
labels = detail_cn_data_header()
|
||||
|
||||
class FileDetailRenderEN(CSVStreamingRenderer):
|
||||
header = detail_file_headers()
|
||||
labels = detail_en_data_header()
|
||||
@@ -1,43 +0,0 @@
|
||||
from django_filters import FilterSet
|
||||
from .models import AsnListModel, AsnDetailModel
|
||||
|
||||
class AsnListFilter(FilterSet):
|
||||
class Meta:
|
||||
model = AsnListModel
|
||||
fields = {
|
||||
"id": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"asn_code": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"asn_status": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"total_weight": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"total_volume": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"total_cost": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"supplier": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"creater": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"is_delete": ['exact', 'iexact'],
|
||||
"create_time": ['exact', 'iexact', 'year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range'],
|
||||
"update_time": ['exact', 'iexact', 'year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range']
|
||||
}
|
||||
|
||||
class AsnDetailFilter(FilterSet):
|
||||
class Meta:
|
||||
model = AsnDetailModel
|
||||
fields = {
|
||||
"id": ['exact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"asn_code": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"asn_status": ['exact', 'iexact'],
|
||||
"supplier": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"goods_code": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"goods_qty": ['exact', 'iexact', 'gt', 'lt', 'gte', 'lte'],
|
||||
"goods_actual_qty": ['exact', 'iexact', 'gt', 'lt', 'gte', 'lte'],
|
||||
"sorted_qty": ['exact', 'iexact', 'gt', 'lt', 'gte', 'lte'],
|
||||
"goods_shortage_qty": ['exact', 'iexact', 'gt', 'lt', 'gte', 'lte'],
|
||||
"goods_more_qty": ['exact', 'iexact', 'gt', 'lt', 'gte', 'lte'],
|
||||
"goods_damage_qty": ['exact', 'iexact', 'gt', 'lt', 'gte', 'lte'],
|
||||
"goods_weight": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"goods_volume": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"goods_cost": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"creater": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"is_delete": ['exact', 'iexact'],
|
||||
"create_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range'],
|
||||
"update_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range']
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
class AsnListModel(models.Model):
|
||||
asn_code = models.CharField(max_length=255, verbose_name="ASN Code")
|
||||
asn_status = models.BigIntegerField(default=1, verbose_name="ASN Status")
|
||||
total_weight = models.FloatField(default=0, verbose_name="Total Weight")
|
||||
total_volume = models.FloatField(default=0, verbose_name="Total Volume")
|
||||
total_cost = models.FloatField(default=0, verbose_name="Total Cost")
|
||||
supplier = models.CharField(max_length=255, verbose_name="ASN Supplier")
|
||||
creater = models.CharField(max_length=255, verbose_name="Who Created")
|
||||
bar_code = models.CharField(max_length=255, verbose_name="Bar Code")
|
||||
openid = models.CharField(max_length=255, verbose_name="Openid")
|
||||
transportation_fee = models.JSONField(default=dict, verbose_name="Transportation Fee")
|
||||
is_delete = models.BooleanField(default=False, verbose_name='Delete Label')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name="Create Time")
|
||||
update_time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name="Update Time")
|
||||
|
||||
class Meta:
|
||||
db_table = 'asnlist'
|
||||
verbose_name = 'data id'
|
||||
verbose_name_plural = "data id"
|
||||
ordering = ['-id']
|
||||
|
||||
def __str__(self):
|
||||
return self.pk
|
||||
|
||||
class AsnDetailModel(models.Model):
|
||||
asn_code = models.CharField(max_length=255, verbose_name="ASN Code")
|
||||
asn_status = models.BigIntegerField(default=1, verbose_name="ASN Status")
|
||||
supplier = models.CharField(max_length=255, verbose_name="ASN Supplier")
|
||||
goods_code = models.CharField(max_length=255, verbose_name="Goods Code")
|
||||
goods_qty = models.BigIntegerField(default=0, verbose_name="Goods QTY")
|
||||
goods_actual_qty = models.BigIntegerField(default=0, verbose_name="Goods Actual QTY")
|
||||
sorted_qty = models.BigIntegerField(default=0, verbose_name="Sorted QTY")
|
||||
goods_shortage_qty = models.BigIntegerField(default=0, verbose_name="Goods Shortage QTY")
|
||||
goods_more_qty = models.BigIntegerField(default=0, verbose_name="Goods More QTY")
|
||||
goods_damage_qty = models.BigIntegerField(default=0, verbose_name="Goods damage QTY")
|
||||
goods_weight = models.FloatField(default=0, verbose_name="Goods Weight")
|
||||
goods_volume = models.FloatField(default=0, verbose_name="Goods Volume")
|
||||
goods_cost = models.FloatField(default=0, verbose_name="Goods Cost")
|
||||
creater = models.CharField(max_length=255, verbose_name="Who Created")
|
||||
openid = models.CharField(max_length=255, verbose_name="Openid")
|
||||
is_delete = models.BooleanField(default=False, verbose_name='Delete Label')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name="Create Time")
|
||||
update_time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name="Update Time")
|
||||
|
||||
class Meta:
|
||||
db_table = 'asndetail'
|
||||
verbose_name = 'data id'
|
||||
verbose_name_plural = "data id"
|
||||
ordering = ['-id']
|
||||
|
||||
def __str__(self):
|
||||
return self.pk
|
||||
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
from collections import OrderedDict
|
||||
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.pagination import PageNumberPagination
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.utils.urls import replace_query_param, remove_query_param
|
||||
|
||||
from supplier.models import ListModel as supplier
|
||||
|
||||
class MyPageNumberPaginationASNList(PageNumberPagination):
|
||||
page_size = 30
|
||||
page_size_query_param = "max_page"
|
||||
max_page_size = 1000
|
||||
page_query_param = 'page'
|
||||
|
||||
def get_previous_link(self):
|
||||
if not self.page.has_previous():
|
||||
return None
|
||||
url = self.request.build_absolute_uri()
|
||||
page_number = self.page.previous_page_number()
|
||||
ssl_check = str(self.request.META.get('HTTP_ORIGIN')).split(':')[0]
|
||||
url_combine = str(url).split(':')
|
||||
if len(str(url).split(':')) == 2:
|
||||
url = ssl_check + ':' + url_combine[1]
|
||||
if page_number == 1:
|
||||
return remove_query_param(url, self.page_query_param)
|
||||
return replace_query_param(url, self.page_query_param, page_number)
|
||||
elif len(str(url).split(':')) == 3:
|
||||
url = ssl_check + ':' + url_combine[1] + ':' + url_combine[2]
|
||||
if page_number == 1:
|
||||
return remove_query_param(url, self.page_query_param)
|
||||
return replace_query_param(url, self.page_query_param, page_number)
|
||||
else:
|
||||
raise APIException({"detail": "Wrong API Url"})
|
||||
|
||||
def get_next_link(self):
|
||||
if not self.page.has_next():
|
||||
return None
|
||||
url = self.request.build_absolute_uri()
|
||||
page_number = self.page.next_page_number()
|
||||
ssl_check = str(self.request.META.get('HTTP_ORIGIN')).split(':')[0]
|
||||
url_combine = str(url).split(':')
|
||||
if len(str(url).split(':')) == 2:
|
||||
url = ssl_check + ':' + url_combine[1]
|
||||
return replace_query_param(url, self.page_query_param, page_number)
|
||||
elif len(str(url).split(':')) == 3:
|
||||
url = ssl_check + ':' + url_combine[1] + ':' + url_combine[2]
|
||||
return replace_query_param(url, self.page_query_param, page_number)
|
||||
else:
|
||||
raise APIException({"detail": "Wrong API Url"})
|
||||
|
||||
def get_paginated_response(self, data):
|
||||
supplier_list_data = supplier.objects.filter(openid=self.request.auth.openid, is_delete=False)
|
||||
supplier_list = []
|
||||
for i in range(len(supplier_list_data)):
|
||||
supplier_list.append(supplier_list_data[i].supplier_name)
|
||||
return Response(OrderedDict([
|
||||
('supplier_list', supplier_list),
|
||||
('count', self.page.paginator.count),
|
||||
('next', self.get_next_link()),
|
||||
('previous', self.get_previous_link()),
|
||||
('results', data)
|
||||
]))
|
||||
@@ -1,156 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
from .models import AsnListModel, AsnDetailModel
|
||||
from utils import datasolve
|
||||
|
||||
class ASNListGetSerializer(serializers.ModelSerializer):
|
||||
asn_code = serializers.CharField(read_only=True, required=False)
|
||||
asn_status = serializers.IntegerField(read_only=True, required=False)
|
||||
supplier = serializers.CharField(read_only=True, required=False)
|
||||
bar_code = serializers.CharField(read_only=True, required=False)
|
||||
creater = serializers.CharField(read_only=True, required=False)
|
||||
create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
class Meta:
|
||||
model = AsnListModel
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', 'openid', ]
|
||||
|
||||
class ASNListPostSerializer(serializers.ModelSerializer):
|
||||
openid = serializers.CharField(read_only=False, required=False, validators=[datasolve.openid_validate])
|
||||
asn_code = serializers.CharField(read_only=False, required=True, validators=[datasolve.asn_data_validate])
|
||||
supplier = serializers.CharField(read_only=False, required=False)
|
||||
bar_code = serializers.CharField(read_only=False, required=True)
|
||||
creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
class Meta:
|
||||
model = AsnListModel
|
||||
exclude = ['is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class ASNListPartialUpdateSerializer(serializers.ModelSerializer):
|
||||
asn_code = serializers.CharField(read_only=False, required=True, validators=[datasolve.asn_data_validate])
|
||||
|
||||
class Meta:
|
||||
model = AsnListModel
|
||||
exclude = ['is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class ASNListUpdateSerializer(serializers.ModelSerializer):
|
||||
asn_code = serializers.CharField(read_only=False, required=True, validators=[datasolve.asn_data_validate])
|
||||
|
||||
class Meta:
|
||||
model = AsnListModel
|
||||
exclude = ['is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class ASNDetailGetSerializer(serializers.ModelSerializer):
|
||||
asn_code = serializers.CharField(read_only=True, required=False)
|
||||
supplier = serializers.CharField(read_only=True, required=False)
|
||||
goods_code = serializers.CharField(read_only=True, required=False)
|
||||
goods_qty = serializers.IntegerField(read_only=True, required=False)
|
||||
goods_actual_qty = serializers.IntegerField(read_only=True, required=False)
|
||||
sorted_qty = serializers.IntegerField(read_only=True, required=False)
|
||||
goods_shortage_qty = serializers.IntegerField(read_only=True, required=False)
|
||||
goods_more_qty = serializers.IntegerField(read_only=True, required=False)
|
||||
goods_damage_qty = serializers.IntegerField(read_only=True, required=False)
|
||||
creater = serializers.CharField(read_only=True, required=False)
|
||||
create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
class Meta:
|
||||
model = AsnDetailModel
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', 'openid']
|
||||
|
||||
class ASNDetailPostSerializer(serializers.ModelSerializer):
|
||||
openid = serializers.CharField(read_only=False, required=False, validators=[datasolve.openid_validate])
|
||||
asn_code = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
supplier = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
goods_code = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
goods_qty = serializers.IntegerField(read_only=False, required=True, validators=[datasolve.qty_0_data_validate])
|
||||
creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
class Meta:
|
||||
model = AsnDetailModel
|
||||
exclude = ['is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class ASNSortedPostSerializer(serializers.ModelSerializer):
|
||||
openid = serializers.CharField(read_only=False, required=False, validators=[datasolve.openid_validate])
|
||||
asn_code = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
supplier = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
goods_code = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
goods_qty = serializers.IntegerField(read_only=False, required=True, validators=[datasolve.qty_data_validate])
|
||||
creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
class Meta:
|
||||
model = AsnDetailModel
|
||||
exclude = ['is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class ASNDetailUpdateSerializer(serializers.ModelSerializer):
|
||||
asn_code = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
supplier = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
goods_code = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
goods_qty = serializers.IntegerField(read_only=False, required=True, validators=[datasolve.qty_0_data_validate])
|
||||
creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
class Meta:
|
||||
model = AsnDetailModel
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class ASNDetailPartialUpdateSerializer(serializers.ModelSerializer):
|
||||
asn_code = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
supplier = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
goods_code = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
goods_qty = serializers.IntegerField(read_only=False, required=False, validators=[datasolve.qty_0_data_validate])
|
||||
creater = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
class Meta:
|
||||
model = AsnDetailModel
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class MoveToBinSerializer(serializers.ModelSerializer):
|
||||
bin_name = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
qty = serializers.IntegerField(read_only=False, required=True, validators=[datasolve.qty_0_data_validate])
|
||||
class Meta:
|
||||
model = AsnDetailModel
|
||||
ref_name = 'AsnMoveToBin'
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class FileListRenderSerializer(serializers.ModelSerializer):
|
||||
asn_code = serializers.CharField(read_only=False, required=False)
|
||||
asn_status = serializers.IntegerField(read_only=False, required=False)
|
||||
total_weight = serializers.FloatField(read_only=False, required=False)
|
||||
total_volume = serializers.FloatField(read_only=False, required=False)
|
||||
total_cost = serializers.FloatField(read_only=False, required=False)
|
||||
supplier = serializers.CharField(read_only=False, required=False)
|
||||
creater = serializers.CharField(read_only=False, required=False)
|
||||
transportation_fee = serializers.JSONField(read_only=False, required=False)
|
||||
create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
|
||||
class Meta:
|
||||
model = AsnListModel
|
||||
ref_name = 'ASNFileListRenderSerializer'
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
|
||||
class FileDetailRenderSerializer(serializers.ModelSerializer):
|
||||
asn_code = serializers.CharField(read_only=False, required=False)
|
||||
asn_status = serializers.IntegerField(read_only=False, required=False)
|
||||
goods_code = serializers.CharField(read_only=False, required=False)
|
||||
goods_qty = serializers.IntegerField(read_only=False, required=False)
|
||||
goods_actual_qty = serializers.IntegerField(read_only=False, required=False)
|
||||
sorted_qty = serializers.IntegerField(read_only=False, required=False)
|
||||
goods_shortage_qty = serializers.IntegerField(read_only=False, required=False)
|
||||
goods_more_qty = serializers.IntegerField(read_only=False, required=False)
|
||||
goods_damage_qty = serializers.IntegerField(read_only=False, required=False)
|
||||
goods_weight = serializers.FloatField(read_only=False, required=False)
|
||||
goods_volume = serializers.FloatField(read_only=False, required=False)
|
||||
goods_cost = serializers.FloatField(read_only=False, required=False)
|
||||
supplier = serializers.CharField(read_only=False, required=False)
|
||||
creater = serializers.CharField(read_only=False, required=False)
|
||||
create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
|
||||
class Meta:
|
||||
model = AsnDetailModel
|
||||
ref_name = 'ASNFileDetailRenderSerializer'
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
from django.urls import path, re_path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path(r'list/', views.AsnListViewSet.as_view({"get": "list", "post": "create"}), name="asnlist"),
|
||||
re_path(r'^list/(?P<pk>\d+)/$', views.AsnListViewSet.as_view({
|
||||
'get': 'retrieve',
|
||||
'put': 'update',
|
||||
'patch': 'partial_update',
|
||||
'delete': 'destroy'
|
||||
}), name="asnlist_1"),
|
||||
path(r'detail/', views.AsnDetailViewSet.as_view({"get": "list", "post": "create", 'put': 'update'}), name="asndetail"),
|
||||
re_path(r'^detail/(?P<pk>\d+)/$', views.AsnDetailViewSet.as_view({
|
||||
'get': 'retrieve',
|
||||
}), name="asndetail_1"),
|
||||
re_path(r'^viewprint/(?P<pk>\d+)/$', views.AsnViewPrintViewSet.as_view({
|
||||
'get': 'retrieve',
|
||||
}), name="asnviewprint_1"),
|
||||
re_path(r'^preload/(?P<pk>\d+)/$', views.AsnPreLoadViewSet.as_view({
|
||||
'post': 'create',
|
||||
}), name="preload_1"),
|
||||
re_path(r'^presort/(?P<pk>\d+)/$', views.AsnPreSortViewSet.as_view({
|
||||
'post': 'create',
|
||||
}), name="presort_1"),
|
||||
path(r'sorted/', views.AsnSortedViewSet.as_view({"put": "update"}), name="sorted"),
|
||||
re_path(r'^sorted/(?P<pk>\d+)/$', views.AsnSortedViewSet.as_view({
|
||||
'post': 'create'
|
||||
}), name="sorted_1"),
|
||||
path(r'movetobin/', views.MoveToBinViewSet.as_view({'put': 'update'}), name="movetobin"),
|
||||
re_path(r'^movetobin/(?P<pk>\d+)/$', views.MoveToBinViewSet.as_view({
|
||||
'post': 'create',
|
||||
}), name="movetobin_1"),
|
||||
path(r'filelist/', views.FileListDownloadView.as_view({"get": "list"}), name="asnfilelistdownload"),
|
||||
path(r'filedetail/', views.FileDetailDownloadView.as_view({"get": "list"}), name="asnfiledetaildownload"),
|
||||
]
|
||||
-1188
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
||||
from django.contrib import admin
|
||||
from . models import ListModel
|
||||
|
||||
admin.site.register(ListModel)
|
||||
@@ -1,38 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
from django.db.models.signals import post_migrate
|
||||
|
||||
class BinpropertyConfig(AppConfig):
|
||||
name = 'binproperty'
|
||||
|
||||
def ready(self):
|
||||
post_migrate.connect(do_init_data, sender=self)
|
||||
|
||||
def do_init_data(sender, **kwargs):
|
||||
init_category()
|
||||
|
||||
def init_category():
|
||||
"""
|
||||
:return:None
|
||||
"""
|
||||
try:
|
||||
from .models import ListModel as ls
|
||||
if ls.objects.filter(openid__iexact='init_data').exists():
|
||||
if ls.objects.filter(openid__iexact='init_data').count() != 4:
|
||||
ls.objects.filter(openid__iexact='init_data').delete()
|
||||
init_data = [
|
||||
ls(id=1, openid='init_data', bin_property='Damage', creater='GreaterWMS'),
|
||||
ls(id=2, openid='init_data', bin_property='Inspection', creater='GreaterWMS'),
|
||||
ls(id=3, openid='init_data', bin_property='Normal', creater='GreaterWMS'),
|
||||
ls(id=4, openid='init_data', bin_property='Holding', creater='GreaterWMS')
|
||||
]
|
||||
ls.objects.bulk_create(init_data, batch_size=100)
|
||||
else:
|
||||
init_data = [
|
||||
ls(id=1, openid='init_data', bin_property='Damage', creater='GreaterWMS'),
|
||||
ls(id=2, openid='init_data', bin_property='Inspection', creater='GreaterWMS'),
|
||||
ls(id=3, openid='init_data', bin_property='Normal', creater='GreaterWMS'),
|
||||
ls(id=4, openid='init_data', bin_property='Holding', creater='GreaterWMS')
|
||||
]
|
||||
ls.objects.bulk_create(init_data, batch_size=100)
|
||||
except:
|
||||
pass
|
||||
@@ -1,14 +0,0 @@
|
||||
from django_filters import FilterSet
|
||||
from .models import ListModel
|
||||
|
||||
class Filter(FilterSet):
|
||||
class Meta:
|
||||
model = ListModel
|
||||
fields = {
|
||||
"id": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"bin_property": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"creater": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"is_delete": ['exact', 'iexact'],
|
||||
"create_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range'],
|
||||
"update_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range']
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
class ListModel(models.Model):
|
||||
bin_property = models.CharField(max_length=32, verbose_name="Bin property")
|
||||
creater = models.CharField(max_length=255, verbose_name="Who created")
|
||||
openid = models.CharField(max_length=255, verbose_name="Openid")
|
||||
is_delete = models.BooleanField(default=False, verbose_name='Delete Label')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name="Create Time")
|
||||
update_time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name="Update Time")
|
||||
|
||||
class Meta:
|
||||
db_table = 'binproperty'
|
||||
verbose_name = 'data id'
|
||||
verbose_name_plural = "data id"
|
||||
ordering = ['bin_property']
|
||||
|
||||
def __str__(self):
|
||||
return self.pk
|
||||
@@ -1,14 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
from .models import ListModel
|
||||
|
||||
class BinpropertyGetSerializer(serializers.ModelSerializer):
|
||||
bin_property = serializers.CharField(read_only=True, required=False)
|
||||
creater = serializers.CharField(read_only=True, required=False)
|
||||
create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
|
||||
class Meta:
|
||||
model = ListModel
|
||||
ref_name = 'BinpropertyGetSerializer'
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', ]
|
||||
@@ -1,6 +0,0 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path(r'', views.APIViewSet.as_view({"get": "list"}), name="binproperty")
|
||||
]
|
||||
@@ -1,29 +0,0 @@
|
||||
from rest_framework import viewsets
|
||||
from .models import ListModel
|
||||
from . import serializers
|
||||
from utils.page import MyPageNumberPagination
|
||||
from rest_framework.filters import OrderingFilter
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from .filter import Filter
|
||||
|
||||
class APIViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
list:
|
||||
Response a data list(all)
|
||||
"""
|
||||
pagination_class = MyPageNumberPagination
|
||||
filter_backends = [DjangoFilterBackend, OrderingFilter, ]
|
||||
ordering_fields = ['id', "create_time", "update_time", ]
|
||||
filter_class = Filter
|
||||
|
||||
def get_queryset(self):
|
||||
if self.request.user:
|
||||
return ListModel.objects.filter(is_delete=False)
|
||||
else:
|
||||
return ListModel.objects.none()
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action in ['list']:
|
||||
return serializers.BinpropertyGetSerializer
|
||||
else:
|
||||
return self.http_method_not_allowed(request=self.request)
|
||||
@@ -1,4 +0,0 @@
|
||||
from django.contrib import admin
|
||||
from . models import ListModel
|
||||
|
||||
admin.site.register(ListModel)
|
||||
@@ -1,4 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
class BinsetConfig(AppConfig):
|
||||
name = 'binset'
|
||||
@@ -1,42 +0,0 @@
|
||||
from rest_framework_csv.renderers import CSVStreamingRenderer
|
||||
|
||||
def file_headers():
|
||||
return [
|
||||
'bin_name',
|
||||
'bin_size',
|
||||
'bin_property',
|
||||
'empty_label',
|
||||
'creater',
|
||||
'create_time',
|
||||
'update_time'
|
||||
]
|
||||
|
||||
def cn_data_header():
|
||||
return dict([
|
||||
('bin_name', u'库位名称'),
|
||||
('bin_size', u'库位尺寸'),
|
||||
('bin_property', u'库位属性'),
|
||||
('empty_label', u'空库位标识'),
|
||||
('creater', u'创建人'),
|
||||
('create_time', u'创建时间'),
|
||||
('update_time', u'更新时间')
|
||||
])
|
||||
|
||||
def en_data_header():
|
||||
return dict([
|
||||
('bin_name', u'Bin Name'),
|
||||
('bin_size', u'Bin Size'),
|
||||
('bin_property', u'Bin Property'),
|
||||
('empty_label', u'Empty Label'),
|
||||
('creater', u'Creater'),
|
||||
('create_time', u'Create Time'),
|
||||
('update_time', u'Update Time')
|
||||
])
|
||||
|
||||
class FileRenderCN(CSVStreamingRenderer):
|
||||
header = file_headers()
|
||||
labels = cn_data_header()
|
||||
|
||||
class FileRenderEN(CSVStreamingRenderer):
|
||||
header = file_headers()
|
||||
labels = en_data_header()
|
||||
@@ -1,17 +0,0 @@
|
||||
from django_filters import FilterSet
|
||||
from .models import ListModel
|
||||
|
||||
class Filter(FilterSet):
|
||||
class Meta:
|
||||
model = ListModel
|
||||
fields = {
|
||||
"id": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range', ],
|
||||
"bin_name": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"bin_size": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"bin_property": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"empty_label": ['exact', 'iexact'],
|
||||
"creater": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"is_delete": ['exact', 'iexact'],
|
||||
"create_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range'],
|
||||
"update_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range']
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
class ListModel(models.Model):
|
||||
bin_name = models.CharField(max_length=255, verbose_name="Bin Name")
|
||||
bin_size = models.CharField(max_length=255, verbose_name="Bin Size")
|
||||
bin_property = models.CharField(max_length=11, verbose_name="Bin Property")
|
||||
empty_label = models.BooleanField(default=True, verbose_name="Empty Label")
|
||||
creater = models.CharField(max_length=255, verbose_name="Who Created")
|
||||
bar_code = models.CharField(max_length=255, verbose_name="Bar Code")
|
||||
openid = models.CharField(max_length=255, verbose_name="Openid")
|
||||
is_delete = models.BooleanField(default=False, verbose_name='Delete Label')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name="Create Time")
|
||||
update_time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name="Update Time")
|
||||
|
||||
class Meta:
|
||||
db_table = 'binset'
|
||||
verbose_name = 'data id'
|
||||
verbose_name_plural = "data id"
|
||||
ordering = ['bin_name']
|
||||
|
||||
def __str__(self):
|
||||
return self.pk
|
||||
@@ -1,70 +0,0 @@
|
||||
from collections import OrderedDict
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework.pagination import PageNumberPagination
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.utils.urls import replace_query_param, remove_query_param
|
||||
from binproperty.models import ListModel as binproperty
|
||||
from binsize.models import ListModel as binsize
|
||||
from django.db.models import Q
|
||||
|
||||
|
||||
class MyPageNumberPagination(PageNumberPagination):
|
||||
page_size = 30
|
||||
page_size_query_param = "max_page"
|
||||
max_page_size = 1000
|
||||
page_query_param = 'page'
|
||||
|
||||
def get_previous_link(self):
|
||||
if not self.page.has_previous():
|
||||
return None
|
||||
url = self.request.build_absolute_uri()
|
||||
page_number = self.page.previous_page_number()
|
||||
ssl_check = str(self.request.META.get('HTTP_ORIGIN')).split(':')[0]
|
||||
url_combine = str(url).split(':')
|
||||
if len(str(url).split(':')) == 2:
|
||||
url = ssl_check + ':' + url_combine[1]
|
||||
if page_number == 1:
|
||||
return remove_query_param(url, self.page_query_param)
|
||||
return replace_query_param(url, self.page_query_param, page_number)
|
||||
elif len(str(url).split(':')) == 3:
|
||||
url = ssl_check + ':' + url_combine[1] + ':' + url_combine[2]
|
||||
if page_number == 1:
|
||||
return remove_query_param(url, self.page_query_param)
|
||||
return replace_query_param(url, self.page_query_param, page_number)
|
||||
else:
|
||||
raise APIException({"detail": "Wrong API Url"})
|
||||
|
||||
def get_next_link(self):
|
||||
if not self.page.has_next():
|
||||
return None
|
||||
url = self.request.build_absolute_uri()
|
||||
page_number = self.page.next_page_number()
|
||||
ssl_check = str(self.request.META.get('HTTP_ORIGIN')).split(':')[0]
|
||||
url_combine = str(url).split(':')
|
||||
if len(str(url).split(':')) == 2:
|
||||
url = ssl_check + ':' + url_combine[1]
|
||||
return replace_query_param(url, self.page_query_param, page_number)
|
||||
elif len(str(url).split(':')) == 3:
|
||||
url = ssl_check + ':' + url_combine[1] + ':' + url_combine[2]
|
||||
return replace_query_param(url, self.page_query_param, page_number)
|
||||
else:
|
||||
raise APIException({"detail": "Wrong API Url"})
|
||||
|
||||
def get_paginated_response(self, data):
|
||||
bin_property_list_data = binproperty.objects.filter(Q(openid=self.request.auth.openid, is_delete=False) |
|
||||
Q(openid='init_data', is_delete=False))
|
||||
bin_property_list = []
|
||||
for i in range(len(bin_property_list_data)):
|
||||
bin_property_list.append(bin_property_list_data[i].bin_property)
|
||||
bin_size_list_data = binsize.objects.filter(openid=self.request.auth.openid, is_delete=False)
|
||||
bin_size_list = []
|
||||
for i in range(len(bin_size_list_data)):
|
||||
bin_size_list.append(bin_size_list_data[i].bin_size)
|
||||
return Response(OrderedDict([
|
||||
('bin_size_list', bin_size_list),
|
||||
('bin_property_list', bin_property_list),
|
||||
('count', self.page.paginator.count),
|
||||
('next', self.get_next_link()),
|
||||
('previous', self.get_previous_link()),
|
||||
('results', data)
|
||||
]))
|
||||
@@ -1,79 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
from .models import ListModel
|
||||
from utils import datasolve
|
||||
|
||||
class ScannerBinsetTagGetSerializer(serializers.ModelSerializer):
|
||||
bin_name = serializers.CharField(read_only=True, required=False)
|
||||
bin_size = serializers.CharField(read_only=True, required=False)
|
||||
bin_property = serializers.CharField(read_only=True, required=False)
|
||||
empty_label = serializers.BooleanField(read_only=True, required=False)
|
||||
creater = serializers.CharField(read_only=True, required=False)
|
||||
bar_code = serializers.CharField(read_only=True, required=False)
|
||||
create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', ]
|
||||
|
||||
|
||||
class BinsetGetSerializer(serializers.ModelSerializer):
|
||||
bin_name = serializers.CharField(read_only=True, required=False)
|
||||
bin_size = serializers.CharField(read_only=True, required=False)
|
||||
bin_property = serializers.CharField(read_only=True, required=False)
|
||||
empty_label = serializers.BooleanField(read_only=True, required=False)
|
||||
creater = serializers.CharField(read_only=True, required=False)
|
||||
bar_code = serializers.CharField(read_only=True, required=False)
|
||||
create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', ]
|
||||
|
||||
class BinsetPostSerializer(serializers.ModelSerializer):
|
||||
openid = serializers.CharField(read_only=False, required=False, validators=[datasolve.openid_validate])
|
||||
bin_name = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
bin_size = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
bin_property = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
bar_code = serializers.CharField(read_only=False, required=True)
|
||||
creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class BinsetUpdateSerializer(serializers.ModelSerializer):
|
||||
bin_name = serializers.CharField(read_only=True, required=False)
|
||||
bin_size = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
bin_property = serializers.CharField(read_only=True, required=False)
|
||||
creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
bar_code = serializers.CharField(read_only=False, required=False)
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class BinsetPartialUpdateSerializer(serializers.ModelSerializer):
|
||||
bin_name = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
bin_size = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
bin_property = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
creater = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class FileRenderSerializer(serializers.ModelSerializer):
|
||||
bin_name = serializers.CharField(read_only=False, required=False)
|
||||
bin_size = serializers.CharField(read_only=False, required=False)
|
||||
bin_property = serializers.CharField(read_only=False, required=False)
|
||||
empty_label = serializers.BooleanField(read_only=False, required=False)
|
||||
creater = serializers.CharField(read_only=False, required=False)
|
||||
create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
|
||||
class Meta:
|
||||
model = ListModel
|
||||
ref_name = 'BinSetFileRenderSerializer'
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
@@ -1,14 +0,0 @@
|
||||
from django.urls import path, re_path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path(r'', views.APIViewSet.as_view({"get": "list", "post": "create"}), name="binset"),
|
||||
path(r'file/', views.FileDownloadView.as_view({"get": "list"}), name="binsetfiledownload"),
|
||||
re_path(r'^(?P<pk>\d+)/$', views.APIViewSet.as_view({
|
||||
'get': 'retrieve',
|
||||
'put': 'update',
|
||||
'patch': 'partial_update',
|
||||
'delete': 'destroy'
|
||||
}), name="binset_1"),
|
||||
path(r'scannerbintag/<str:bar_code>/',views.ScannerBinsetTagView.as_view({"get":"retrieve"}))
|
||||
]
|
||||
-236
@@ -1,236 +0,0 @@
|
||||
from rest_framework import viewsets
|
||||
from .models import ListModel
|
||||
from . import serializers
|
||||
from .page import MyPageNumberPagination
|
||||
from rest_framework.filters import OrderingFilter
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework.response import Response
|
||||
from .filter import Filter
|
||||
from rest_framework.exceptions import APIException
|
||||
from django.db.models import Q
|
||||
from binsize.models import ListModel as binsize
|
||||
from scanner.models import ListModel as scanner
|
||||
from binproperty.models import ListModel as binproperty
|
||||
from .serializers import FileRenderSerializer
|
||||
from django.http import StreamingHttpResponse
|
||||
from .files import FileRenderCN, FileRenderEN
|
||||
from rest_framework.settings import api_settings
|
||||
from utils.md5 import Md5
|
||||
from .serializers import ScannerBinsetTagGetSerializer
|
||||
|
||||
class ScannerBinsetTagView(viewsets.ModelViewSet):
|
||||
"""
|
||||
retrieve:
|
||||
Response a data list(get)
|
||||
|
||||
http://127.0.0.1:8008/binset/scannerbintag/3d89ad23d185d5f206d860745c5c4121/
|
||||
"""
|
||||
pagination_class = MyPageNumberPagination
|
||||
filter_backends = [DjangoFilterBackend, OrderingFilter, ]
|
||||
ordering_fields = ['id', "create_time", "update_time", ]
|
||||
filter_class = Filter
|
||||
lookup_field = 'bar_code'
|
||||
def get_project(self):
|
||||
try:
|
||||
bar_code = self.kwargs['bar_code']
|
||||
return bar_code
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_queryset(self):
|
||||
bar_code = self.get_project()
|
||||
if self.request.user:
|
||||
if bar_code is None:
|
||||
return ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False)
|
||||
else:
|
||||
return ListModel.objects.filter(openid=self.request.auth.openid, bar_code=bar_code, is_delete=False)
|
||||
else:
|
||||
return ListModel.objects.none()
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action in ['list', 'retrieve', 'destroy']:
|
||||
return serializers.ScannerBinsetTagGetSerializer
|
||||
else:
|
||||
return self.http_method_not_allowed(request=self.request)
|
||||
|
||||
|
||||
|
||||
|
||||
class APIViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
retrieve:
|
||||
Response a data list(get)
|
||||
|
||||
list:
|
||||
Response a data list(all)
|
||||
|
||||
create:
|
||||
Create a data line(post)
|
||||
|
||||
delete:
|
||||
Delete a data line(delete)
|
||||
|
||||
partial_update:
|
||||
Partial_update a data(patch:partial_update)
|
||||
|
||||
update:
|
||||
Update a data(put:update)
|
||||
"""
|
||||
pagination_class = MyPageNumberPagination
|
||||
filter_backends = [DjangoFilterBackend, OrderingFilter, ]
|
||||
ordering_fields = ['id', "create_time", "update_time", ]
|
||||
filter_class = Filter
|
||||
|
||||
def get_project(self):
|
||||
try:
|
||||
id = self.kwargs.get('pk')
|
||||
return id
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_queryset(self):
|
||||
id = self.get_project()
|
||||
if self.request.user:
|
||||
if id is None:
|
||||
return ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False)
|
||||
else:
|
||||
return ListModel.objects.filter(openid=self.request.auth.openid, id=id, is_delete=False)
|
||||
else:
|
||||
return ListModel.objects.none()
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action in ['list', 'retrieve', 'destroy']:
|
||||
return serializers.BinsetGetSerializer
|
||||
elif self.action in ['create']:
|
||||
return serializers.BinsetPostSerializer
|
||||
elif self.action in ['update']:
|
||||
return serializers.BinsetUpdateSerializer
|
||||
elif self.action in ['partial_update']:
|
||||
return serializers.BinsetPartialUpdateSerializer
|
||||
else:
|
||||
return self.http_method_not_allowed(request=self.request)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
data = self.request.data
|
||||
data['openid'] = self.request.auth.openid
|
||||
if ListModel.objects.filter(openid=data['openid'], bin_name=data['bin_name'], is_delete=False).exists():
|
||||
raise APIException({"detail": "Data exists"})
|
||||
else:
|
||||
if binsize.objects.filter(openid=data['openid'], bin_size=data['bin_size'], is_delete=False).exists():
|
||||
if binproperty.objects.filter(Q(openid=data['openid'], bin_property=data['bin_property'], is_delete=False) |
|
||||
Q(openid='init_data', bin_property=data['bin_property'], is_delete=False)).exists():
|
||||
data['bar_code'] = Md5.md5(data['bin_name'])
|
||||
serializer = self.get_serializer(data=data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
scanner.objects.create(openid=self.request.auth.openid, mode="BINSET", code=data['bin_name'],
|
||||
bar_code=data['bar_code'])
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=200, headers=headers)
|
||||
else:
|
||||
raise APIException({"detail": "Bin property does not exists or it has been changed"})
|
||||
else:
|
||||
raise APIException({"detail": "Bin size does not exists or it has been changed"})
|
||||
|
||||
def update(self, request, pk):
|
||||
qs = self.get_object()
|
||||
if qs.openid != self.request.auth.openid:
|
||||
raise APIException({"detail": "Cannot update data which not yours"})
|
||||
else:
|
||||
data = self.request.data
|
||||
if binsize.objects.filter(openid=self.request.auth.openid, bin_size=data['bin_size'], is_delete=False).exists():
|
||||
if binproperty.objects.filter(Q(openid=self.request.auth.openid, bin_property=data['bin_property'], is_delete=False) |
|
||||
Q(openid='init_data', bin_property=data['bin_property'], is_delete=False)).exists():
|
||||
serializer = self.get_serializer(qs, data=data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=200, headers=headers)
|
||||
else:
|
||||
raise APIException({"detail": "Bin property does not exists or it has been changed"})
|
||||
else:
|
||||
raise APIException({"detail": "Bin size does not exists or it has been changed"})
|
||||
|
||||
def partial_update(self, request, pk):
|
||||
qs = self.get_object()
|
||||
if qs.openid != self.request.auth.openid:
|
||||
raise APIException({"detail": "Cannot partial_update data which not yours"})
|
||||
else:
|
||||
data = self.request.data
|
||||
if binsize.objects.filter(openid=self.request.auth.openid, bin_size=data['bin_size'], is_delete=False).exists():
|
||||
if binproperty.objects.filter(Q(openid=self.request.auth.openid, bin_property=data['bin_property'], is_delete=False) |
|
||||
Q(openid='init_data', bin_property=data['bin_property'], is_delete=False)).exists():
|
||||
serializer = self.get_serializer(qs, data=data, partial=True)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=200, headers=headers)
|
||||
else:
|
||||
raise APIException({"detail": "Bin property does not exists or it has been changed"})
|
||||
else:
|
||||
raise APIException({"detail": "Bin size does not exists or it has been changed"})
|
||||
|
||||
def destroy(self, request, pk):
|
||||
qs = self.get_object()
|
||||
if qs.openid != self.request.auth.openid:
|
||||
raise APIException({"detail": "Cannot delete data which not yours"})
|
||||
else:
|
||||
qs.is_delete = True
|
||||
qs.save()
|
||||
serializer = self.get_serializer(qs, many=False)
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=200, headers=headers)
|
||||
|
||||
class FileDownloadView(viewsets.ModelViewSet):
|
||||
renderer_classes = (FileRenderCN, ) + tuple(api_settings.DEFAULT_RENDERER_CLASSES)
|
||||
filter_backends = [DjangoFilterBackend, OrderingFilter, ]
|
||||
ordering_fields = ['id', "create_time", "update_time", ]
|
||||
filter_class = Filter
|
||||
|
||||
def get_project(self):
|
||||
try:
|
||||
id = self.kwargs.get('pk')
|
||||
return id
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_queryset(self):
|
||||
id = self.get_project()
|
||||
if self.request.user:
|
||||
if id is None:
|
||||
return ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False)
|
||||
else:
|
||||
return ListModel.objects.filter(openid=self.request.auth.openid, id=id, is_delete=False)
|
||||
else:
|
||||
return ListModel.objects.none()
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action in ['list']:
|
||||
return serializers.FileRenderSerializer
|
||||
else:
|
||||
return self.http_method_not_allowed(request=self.request)
|
||||
|
||||
def get_lang(self, data):
|
||||
lang = self.request.META.get('HTTP_LANGUAGE')
|
||||
if lang:
|
||||
if lang == 'zh-hans':
|
||||
return FileRenderCN().render(data)
|
||||
else:
|
||||
return FileRenderEN().render(data)
|
||||
else:
|
||||
return FileRenderEN().render(data)
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
from datetime import datetime
|
||||
dt = datetime.now()
|
||||
data = (
|
||||
FileRenderSerializer(instance).data
|
||||
for instance in self.filter_queryset(self.get_queryset())
|
||||
)
|
||||
renderer = self.get_lang(data)
|
||||
response = StreamingHttpResponse(
|
||||
renderer,
|
||||
content_type="text/csv"
|
||||
)
|
||||
response['Content-Disposition'] = "attachment; filename='binset_{}.csv'".format(str(dt.strftime('%Y%m%d%H%M%S%f')))
|
||||
return response
|
||||
@@ -1,4 +0,0 @@
|
||||
from django.contrib import admin
|
||||
from . models import ListModel
|
||||
|
||||
admin.site.register(ListModel)
|
||||
@@ -1,4 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
class BinsizeConfig(AppConfig):
|
||||
name = 'binsize'
|
||||
@@ -1,42 +0,0 @@
|
||||
from rest_framework_csv.renderers import CSVStreamingRenderer
|
||||
|
||||
def file_headers():
|
||||
return [
|
||||
'bin_size',
|
||||
'bin_size_w',
|
||||
'bin_size_d',
|
||||
'bin_size_h',
|
||||
'creater',
|
||||
'create_time',
|
||||
'update_time'
|
||||
]
|
||||
|
||||
def cn_data_header():
|
||||
return dict([
|
||||
('bin_size', u'库位尺寸名称'),
|
||||
('bin_size_w', u'库位尺寸长度'),
|
||||
('bin_size_d', u'库位尺寸宽度'),
|
||||
('bin_size_h', u'库位尺寸高度'),
|
||||
('creater', u'创建人'),
|
||||
('create_time', u'创建时间'),
|
||||
('update_time', u'更新时间'),
|
||||
])
|
||||
|
||||
def en_data_header():
|
||||
return dict([
|
||||
('bin_size', u'Bin Size'),
|
||||
('bin_size_w', u'Bin Wide'),
|
||||
('bin_size_d', u'Bin Depth'),
|
||||
('bin_size_h', u'Bin Height'),
|
||||
('creater', u'Creater'),
|
||||
('create_time', u'Create Time'),
|
||||
('update_time', u'Update Time'),
|
||||
])
|
||||
|
||||
class FileRenderCN(CSVStreamingRenderer):
|
||||
header = file_headers()
|
||||
labels = cn_data_header()
|
||||
|
||||
class FileRenderEN(CSVStreamingRenderer):
|
||||
header = file_headers()
|
||||
labels = en_data_header()
|
||||
@@ -1,17 +0,0 @@
|
||||
from django_filters import FilterSet
|
||||
from .models import ListModel
|
||||
|
||||
class Filter(FilterSet):
|
||||
class Meta:
|
||||
model = ListModel
|
||||
fields = {
|
||||
"id": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"bin_size": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"bin_size_w": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"bin_size_d": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"bin_size_h": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"creater": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"is_delete": ['exact', 'iexact'],
|
||||
"create_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range'],
|
||||
"update_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range']
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
class ListModel(models.Model):
|
||||
bin_size = models.CharField(max_length=255, verbose_name="Bin Name")
|
||||
bin_size_w = models.FloatField(default=0, verbose_name="Bin Width")
|
||||
bin_size_d = models.FloatField(default=0, verbose_name="Bin Depth")
|
||||
bin_size_h = models.FloatField(default=0, verbose_name="Bin Height")
|
||||
creater = models.CharField(max_length=255, verbose_name="Who created")
|
||||
openid = models.CharField(max_length=255, verbose_name="Openid")
|
||||
is_delete = models.BooleanField(default=False, verbose_name='Delete Label')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name="Create Time")
|
||||
update_time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name="Update Time")
|
||||
|
||||
class Meta:
|
||||
db_table = 'binsize'
|
||||
verbose_name = 'data id'
|
||||
verbose_name_plural = "data id"
|
||||
ordering = ['-id']
|
||||
|
||||
def __str__(self):
|
||||
return self.pk
|
||||
@@ -1,64 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
from .models import ListModel
|
||||
from utils import datasolve
|
||||
|
||||
class BinsizeGetSerializer(serializers.ModelSerializer):
|
||||
bin_size = serializers.CharField(read_only=True, required=False)
|
||||
bin_size_w = serializers.FloatField(read_only=True, required=False)
|
||||
bin_size_d = serializers.FloatField(read_only=True, required=False)
|
||||
bin_size_h = serializers.FloatField(read_only=True, required=False)
|
||||
creater = serializers.CharField(read_only=True, required=False)
|
||||
create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', ]
|
||||
|
||||
class BinsizePostSerializer(serializers.ModelSerializer):
|
||||
openid = serializers.CharField(read_only=False, required=False, validators=[datasolve.openid_validate])
|
||||
bin_size = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
bin_size_w = serializers.FloatField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
bin_size_d = serializers.FloatField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
bin_size_h = serializers.FloatField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class BinsizeUpdateSerializer(serializers.ModelSerializer):
|
||||
bin_size = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
bin_size_w = serializers.FloatField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
bin_size_d = serializers.FloatField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
bin_size_h = serializers.FloatField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class BinsizePartialUpdateSerializer(serializers.ModelSerializer):
|
||||
bin_size = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
bin_size_w = serializers.FloatField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
bin_size_d = serializers.FloatField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
bin_size_h = serializers.FloatField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
creater = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class FileRenderSerializer(serializers.ModelSerializer):
|
||||
bin_size = serializers.CharField(read_only=False, required=False)
|
||||
bin_size_w = serializers.FloatField(read_only=False, required=False)
|
||||
bin_size_d = serializers.FloatField(read_only=False, required=False)
|
||||
bin_size_h = serializers.FloatField(read_only=False, required=False)
|
||||
creater = serializers.CharField(read_only=False, required=False)
|
||||
create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
|
||||
class Meta:
|
||||
model = ListModel
|
||||
ref_name = 'BinSizeFileRenderSerializer'
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
@@ -1,13 +0,0 @@
|
||||
from django.urls import path, re_path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path(r'', views.APIViewSet.as_view({"get": "list", "post": "create"}), name="binsize"),
|
||||
path(r'file/', views.FileDownloadView.as_view({"get": "list"}), name="binsizefiledownload"),
|
||||
re_path(r'^(?P<pk>\d+)/$', views.APIViewSet.as_view({
|
||||
'get': 'retrieve',
|
||||
'put': 'update',
|
||||
'patch': 'partial_update',
|
||||
'delete': 'destroy'
|
||||
}), name="binsize_1")
|
||||
]
|
||||
@@ -1,168 +0,0 @@
|
||||
from rest_framework import viewsets
|
||||
from .models import ListModel
|
||||
from . import serializers
|
||||
from utils.page import MyPageNumberPagination
|
||||
from rest_framework.filters import OrderingFilter
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework.response import Response
|
||||
from .filter import Filter
|
||||
from rest_framework.exceptions import APIException
|
||||
from .serializers import FileRenderSerializer
|
||||
from django.http import StreamingHttpResponse
|
||||
from .files import FileRenderCN, FileRenderEN
|
||||
from rest_framework.settings import api_settings
|
||||
|
||||
class APIViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
retrieve:
|
||||
Response a data list(get)
|
||||
|
||||
list:
|
||||
Response a data list(all)
|
||||
|
||||
create:
|
||||
Create a data line(post)
|
||||
|
||||
delete:
|
||||
Delete a data line(delete)
|
||||
|
||||
partial_update:
|
||||
Partial_update a data(patch:partial_update)
|
||||
|
||||
update:
|
||||
Update a data(put:update)
|
||||
"""
|
||||
pagination_class = MyPageNumberPagination
|
||||
filter_backends = [DjangoFilterBackend, OrderingFilter, ]
|
||||
ordering_fields = ['id', "create_time", "update_time", ]
|
||||
filter_class = Filter
|
||||
|
||||
def get_project(self):
|
||||
try:
|
||||
id = self.kwargs.get('pk')
|
||||
return id
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_queryset(self):
|
||||
id = self.get_project()
|
||||
if self.request.user:
|
||||
if id is None:
|
||||
return ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False)
|
||||
else:
|
||||
return ListModel.objects.filter(openid=self.request.auth.openid, id=id, is_delete=False)
|
||||
else:
|
||||
return ListModel.objects.none()
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action in ['list', 'retrieve', 'destroy']:
|
||||
return serializers.BinsizeGetSerializer
|
||||
elif self.action in ['create']:
|
||||
return serializers.BinsizePostSerializer
|
||||
elif self.action in ['update']:
|
||||
return serializers.BinsizeUpdateSerializer
|
||||
elif self.action in ['partial_update']:
|
||||
return serializers.BinsizePartialUpdateSerializer
|
||||
else:
|
||||
return self.http_method_not_allowed(request=self.request)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
data = self.request.data
|
||||
data['openid'] = self.request.auth.openid
|
||||
if ListModel.objects.filter(openid=data['openid'], bin_size=data['bin_size'], is_delete=False).exists():
|
||||
raise APIException({"detail": "Data exists"})
|
||||
else:
|
||||
serializer = self.get_serializer(data=data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=200, headers=headers)
|
||||
|
||||
def update(self, request, pk):
|
||||
qs = self.get_object()
|
||||
if qs.openid != self.request.auth.openid:
|
||||
raise APIException({"detail": "Cannot update data which not yours"})
|
||||
else:
|
||||
data = self.request.data
|
||||
serializer = self.get_serializer(qs, data=data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=200, headers=headers)
|
||||
|
||||
def partial_update(self, request, pk):
|
||||
qs = self.get_object()
|
||||
if qs.openid != self.request.auth.openid:
|
||||
raise APIException({"detail": "Cannot partial_update data which not yours"})
|
||||
else:
|
||||
data = self.request.data
|
||||
serializer = self.get_serializer(qs, data=data, partial=True)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=200, headers=headers)
|
||||
|
||||
def destroy(self, request, pk):
|
||||
qs = self.get_object()
|
||||
if qs.openid != self.request.auth.openid:
|
||||
raise APIException({"detail": "Cannot delete data which not yours"})
|
||||
else:
|
||||
qs.is_delete = True
|
||||
qs.save()
|
||||
serializer = self.get_serializer(qs, many=False)
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=200, headers=headers)
|
||||
|
||||
class FileDownloadView(viewsets.ModelViewSet):
|
||||
renderer_classes = (FileRenderCN, ) + tuple(api_settings.DEFAULT_RENDERER_CLASSES)
|
||||
filter_backends = [DjangoFilterBackend, OrderingFilter, ]
|
||||
ordering_fields = ['id', "create_time", "update_time", ]
|
||||
filter_class = Filter
|
||||
|
||||
def get_project(self):
|
||||
try:
|
||||
id = self.kwargs.get('pk')
|
||||
return id
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_queryset(self):
|
||||
id = self.get_project()
|
||||
if self.request.user:
|
||||
if id is None:
|
||||
return ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False)
|
||||
else:
|
||||
return ListModel.objects.filter(openid=self.request.auth.openid, id=id, is_delete=False)
|
||||
else:
|
||||
return ListModel.objects.none()
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action in ['list']:
|
||||
return serializers.FileRenderSerializer
|
||||
else:
|
||||
return self.http_method_not_allowed(request=self.request)
|
||||
|
||||
def get_lang(self, data):
|
||||
lang = self.request.META.get('HTTP_LANGUAGE')
|
||||
if lang:
|
||||
if lang == 'zh-hans':
|
||||
return FileRenderCN().render(data)
|
||||
else:
|
||||
return FileRenderEN().render(data)
|
||||
else:
|
||||
return FileRenderEN().render(data)
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
from datetime import datetime
|
||||
dt = datetime.now()
|
||||
data = (
|
||||
FileRenderSerializer(instance).data
|
||||
for instance in self.filter_queryset(self.get_queryset())
|
||||
)
|
||||
renderer = self.get_lang(data)
|
||||
response = StreamingHttpResponse(
|
||||
renderer,
|
||||
content_type="text/csv"
|
||||
)
|
||||
response['Content-Disposition'] = "attachment; filename='binsize_{}.csv'".format(str(dt.strftime('%Y%m%d%H%M%S%f')))
|
||||
return response
|
||||
@@ -1,4 +0,0 @@
|
||||
from django.contrib import admin
|
||||
from . models import ListModel
|
||||
|
||||
admin.site.register(ListModel)
|
||||
@@ -1,4 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
class CapitalConfig(AppConfig):
|
||||
name = 'capital'
|
||||
@@ -1,38 +0,0 @@
|
||||
from rest_framework_csv.renderers import CSVStreamingRenderer
|
||||
|
||||
def file_headers():
|
||||
return [
|
||||
'capital_name',
|
||||
'capital_qty',
|
||||
'capital_cost',
|
||||
'creater',
|
||||
'create_time',
|
||||
'update_time'
|
||||
]
|
||||
def cn_data_header():
|
||||
return dict([
|
||||
('capital_name', u'资产名称'),
|
||||
('capital_qty', u'资产数量'),
|
||||
('capital_cost', u'资产成本'),
|
||||
('creater', u'创建人'),
|
||||
('create_time', u'创建时间'),
|
||||
('update_time', u'更新时间')
|
||||
])
|
||||
|
||||
def en_data_header():
|
||||
return dict([
|
||||
('capital_name', u'Capital Name'),
|
||||
('capital_qty', u'Capital Qty'),
|
||||
('capital_cost', u'Capital Cost'),
|
||||
('creater', u'Creater'),
|
||||
('create_time', u'Create Time'),
|
||||
('update_time', u'Update Time')
|
||||
])
|
||||
|
||||
class FileRenderCN(CSVStreamingRenderer):
|
||||
header = file_headers()
|
||||
labels = cn_data_header()
|
||||
|
||||
class FileRenderEN(CSVStreamingRenderer):
|
||||
header = file_headers()
|
||||
labels = en_data_header()
|
||||
@@ -1,16 +0,0 @@
|
||||
from django_filters import FilterSet
|
||||
from .models import ListModel
|
||||
|
||||
class Filter(FilterSet):
|
||||
class Meta:
|
||||
model = ListModel
|
||||
fields = {
|
||||
"id": ['exact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"capital_name": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"capital_qty": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"capital_cost": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"creater": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"is_delete": ['exact', 'iexact'],
|
||||
"create_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range'],
|
||||
"update_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range']
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
class ListModel(models.Model):
|
||||
capital_name = models.CharField(max_length=255, verbose_name="Capital Name")
|
||||
capital_qty = models.BigIntegerField(default=0, verbose_name="Capital Qty")
|
||||
capital_cost = models.FloatField(default=0, verbose_name="Capital Cost")
|
||||
creater = models.CharField(max_length=255, verbose_name="Who Created")
|
||||
openid = models.CharField(max_length=255, verbose_name="Openid")
|
||||
is_delete = models.BooleanField(default=False, verbose_name='Delete Label')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name="Create Time")
|
||||
update_time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name="Update Time")
|
||||
|
||||
class Meta:
|
||||
db_table = 'capital'
|
||||
verbose_name = 'data id'
|
||||
verbose_name_plural = "data id"
|
||||
ordering = ['-id']
|
||||
|
||||
def __str__(self):
|
||||
return self.pk
|
||||
@@ -1,59 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
from .models import ListModel
|
||||
from utils import datasolve
|
||||
|
||||
class CapitalGetSerializer(serializers.ModelSerializer):
|
||||
capital_name = serializers.CharField(read_only=True, required=False)
|
||||
capital_qty = serializers.IntegerField(read_only=True, required=False)
|
||||
capital_cost = serializers.FloatField(read_only=True, required=False)
|
||||
creater = serializers.CharField(read_only=True, required=False)
|
||||
create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', 'openid', ]
|
||||
|
||||
class CapitalPostSerializer(serializers.ModelSerializer):
|
||||
openid = serializers.CharField(read_only=False, required=False, validators=[datasolve.openid_validate])
|
||||
capital_name = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
capital_qty = serializers.IntegerField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
capital_cost = serializers.FloatField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class CapitalUpdateSerializer(serializers.ModelSerializer):
|
||||
capital_name = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
capital_qty = serializers.IntegerField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
capital_cost = serializers.FloatField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class CapitalPartialUpdateSerializer(serializers.ModelSerializer):
|
||||
capital_name = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
capital_qty = serializers.IntegerField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
capital_cost = serializers.FloatField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class FileRenderSerializer(serializers.ModelSerializer):
|
||||
capital_name = serializers.CharField(read_only=False, required=False)
|
||||
capital_qty = serializers.IntegerField(read_only=False, required=False)
|
||||
capital_cost = serializers.FloatField(read_only=False, required=False)
|
||||
creater = serializers.CharField(read_only=False, required=False)
|
||||
create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
|
||||
class Meta:
|
||||
model = ListModel
|
||||
ref_name = 'CapitalFileRenderSerializer'
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
@@ -1,13 +0,0 @@
|
||||
from django.urls import path, re_path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path(r'', views.APIViewSet.as_view({"get": "list", "post": "create"}), name="capital"),
|
||||
path(r'file/', views.FileDownloadView.as_view({"get": "list"}), name="capitalfiledownload"),
|
||||
re_path(r'^(?P<pk>\d+)/$', views.APIViewSet.as_view({
|
||||
'get': 'retrieve',
|
||||
'put': 'update',
|
||||
'patch': 'partial_update',
|
||||
'delete': 'destroy'
|
||||
}), name="capital_1")
|
||||
]
|
||||
@@ -1,165 +0,0 @@
|
||||
from rest_framework import viewsets
|
||||
from .models import ListModel
|
||||
from . import serializers
|
||||
from utils.page import MyPageNumberPagination
|
||||
from rest_framework.filters import OrderingFilter
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework.response import Response
|
||||
from .filter import Filter
|
||||
from rest_framework.exceptions import APIException
|
||||
from .serializers import FileRenderSerializer
|
||||
from django.http import StreamingHttpResponse
|
||||
from .files import FileRenderCN, FileRenderEN
|
||||
from rest_framework.settings import api_settings
|
||||
|
||||
class APIViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
retrieve:
|
||||
Response a data list(get)
|
||||
|
||||
list:
|
||||
Response a data list(all)
|
||||
|
||||
create:
|
||||
Create a data line(post)
|
||||
|
||||
delete:
|
||||
Delete a data line(delete)
|
||||
|
||||
partial_update:
|
||||
Partial_update a data(patch:partial_update)
|
||||
|
||||
update:
|
||||
Update a data(put:update)
|
||||
"""
|
||||
pagination_class = MyPageNumberPagination
|
||||
filter_backends = [DjangoFilterBackend, OrderingFilter, ]
|
||||
ordering_fields = ['id', "create_time", "update_time", ]
|
||||
filter_class = Filter
|
||||
|
||||
def get_project(self):
|
||||
try:
|
||||
id = self.kwargs.get('pk')
|
||||
return id
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_queryset(self):
|
||||
id = self.get_project()
|
||||
if self.request.user:
|
||||
if id is None:
|
||||
return ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False)
|
||||
else:
|
||||
return ListModel.objects.filter(openid=self.request.auth.openid, id=id, is_delete=False)
|
||||
else:
|
||||
return ListModel.objects.none()
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action in ['list', 'retrieve', 'destroy']:
|
||||
return serializers.CapitalGetSerializer
|
||||
elif self.action in ['create']:
|
||||
return serializers.CapitalPostSerializer
|
||||
elif self.action in ['update']:
|
||||
return serializers.CapitalUpdateSerializer
|
||||
elif self.action in ['partial_update']:
|
||||
return serializers.CapitalPartialUpdateSerializer
|
||||
else:
|
||||
return self.http_method_not_allowed(request=self.request)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
data = self.request.data
|
||||
data['openid'] = self.request.auth.openid
|
||||
serializer = self.get_serializer(data=data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=200, headers=headers)
|
||||
|
||||
def update(self, request, pk):
|
||||
qs = self.get_object()
|
||||
if qs.openid != self.request.auth.openid:
|
||||
raise APIException({"detail": "Cannot update data which not yours"})
|
||||
else:
|
||||
data = self.request.data
|
||||
serializer = self.get_serializer(qs, data=data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=200, headers=headers)
|
||||
|
||||
def partial_update(self, request, pk):
|
||||
qs = self.get_object()
|
||||
if qs.openid != self.request.auth.openid:
|
||||
raise APIException({"detail": "Cannot partial_update data which not yours"})
|
||||
else:
|
||||
data = self.request.data
|
||||
serializer = self.get_serializer(qs, data=data, partial=True)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=200, headers=headers)
|
||||
|
||||
def destroy(self, request, pk):
|
||||
qs = self.get_object()
|
||||
if qs.openid != self.request.auth.openid:
|
||||
raise APIException({"detail": "Cannot delete data which not yours"})
|
||||
else:
|
||||
qs.is_delete = True
|
||||
qs.save()
|
||||
serializer = self.get_serializer(qs, many=False)
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=200, headers=headers)
|
||||
|
||||
class FileDownloadView(viewsets.ModelViewSet):
|
||||
renderer_classes = (FileRenderCN, ) + tuple(api_settings.DEFAULT_RENDERER_CLASSES)
|
||||
filter_backends = [DjangoFilterBackend, OrderingFilter, ]
|
||||
ordering_fields = ['id', "create_time", "update_time", ]
|
||||
filter_class = Filter
|
||||
|
||||
def get_project(self):
|
||||
try:
|
||||
id = self.kwargs.get('pk')
|
||||
return id
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_queryset(self):
|
||||
id = self.get_project()
|
||||
if self.request.user:
|
||||
if id is None:
|
||||
return ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False)
|
||||
else:
|
||||
return ListModel.objects.filter(openid=self.request.auth.openid, id=id, is_delete=False)
|
||||
else:
|
||||
return ListModel.objects.none()
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action in ['list']:
|
||||
return serializers.FileRenderSerializer
|
||||
else:
|
||||
return self.http_method_not_allowed(request=self.request)
|
||||
|
||||
def get_lang(self, data):
|
||||
lang = self.request.META.get('HTTP_LANGUAGE')
|
||||
if lang:
|
||||
if lang == 'zh-hans':
|
||||
return FileRenderCN().render(data)
|
||||
else:
|
||||
return FileRenderEN().render(data)
|
||||
else:
|
||||
return FileRenderEN().render(data)
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
from datetime import datetime
|
||||
dt = datetime.now()
|
||||
data = (
|
||||
FileRenderSerializer(instance).data
|
||||
for instance in self.filter_queryset(self.get_queryset())
|
||||
)
|
||||
renderer = self.get_lang(data)
|
||||
response = StreamingHttpResponse(
|
||||
renderer,
|
||||
content_type="text/csv"
|
||||
)
|
||||
response['Content-Disposition'] = "attachment; filename='capital_{}.csv'".format(str(dt.strftime('%Y%m%d%H%M%S%f')))
|
||||
return response
|
||||
@@ -1,4 +0,0 @@
|
||||
from django.contrib import admin
|
||||
from . models import ListModel
|
||||
|
||||
admin.site.register(ListModel)
|
||||
@@ -1,5 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ChatConfig(AppConfig):
|
||||
name = 'chat'
|
||||
@@ -1,14 +0,0 @@
|
||||
from django_filters import FilterSet
|
||||
from .models import ListModel
|
||||
|
||||
class Filter(FilterSet):
|
||||
class Meta:
|
||||
model = ListModel
|
||||
fields = {
|
||||
"id": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"read": ['exact', 'iexact'],
|
||||
"detail": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"is_delete": ['exact', 'iexact'],
|
||||
"create_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range'],
|
||||
"update_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range']
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
class ListModel(models.Model):
|
||||
sender = models.CharField(max_length=100, verbose_name='Sender')
|
||||
receiver = models.CharField(max_length=100, verbose_name='Receiver')
|
||||
read = models.BooleanField(default=False, verbose_name="Readed")
|
||||
detail = models.CharField(max_length=100, verbose_name='Chat text')
|
||||
is_delete = models.BooleanField(default=False, verbose_name='Delete label')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name='Create time')
|
||||
update_time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name='Update time')
|
||||
|
||||
class Meta:
|
||||
db_table = 'chat'
|
||||
verbose_name = 'data id'
|
||||
verbose_name_plural = "data id"
|
||||
ordering = ['-id']
|
||||
|
||||
def __str__(self):
|
||||
return self.pk
|
||||
@@ -1,14 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
from .models import ListModel
|
||||
|
||||
class ChatGetSerializer(serializers.ModelSerializer):
|
||||
sender = serializers.CharField(read_only=True, required=False)
|
||||
receiver = serializers.CharField(read_only=True, required=False)
|
||||
read = serializers.BooleanField(read_only=True, required=False)
|
||||
detail = serializers.CharField(read_only=True, required=False)
|
||||
create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['is_delete', ]
|
||||
read_only_fields = ['id', ]
|
||||
@@ -1,7 +0,0 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path(r'', views.ChatViewSet.as_view({"get": "list"}), name="chat"),
|
||||
path(r'read/', views.ReadAPI.as_view({"get": "list"}), name='read')
|
||||
]
|
||||
@@ -1,57 +0,0 @@
|
||||
from django.db.models import Q
|
||||
from rest_framework import viewsets
|
||||
from .models import ListModel
|
||||
from . import serializers
|
||||
from utils.page import MyPageNumberPagination
|
||||
from rest_framework.filters import OrderingFilter
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from .filter import Filter
|
||||
|
||||
class ChatViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
list:
|
||||
Response a data list(all)
|
||||
"""
|
||||
pagination_class = MyPageNumberPagination
|
||||
filter_backends = [DjangoFilterBackend, OrderingFilter, ]
|
||||
ordering_fields = ['id', "create_time", "update_time", ]
|
||||
filter_class = Filter
|
||||
|
||||
def get_queryset(self):
|
||||
if self.request.user:
|
||||
sender = str(self.request.GET.get('sender', '')) + '-' + self.request.auth.openid
|
||||
receiver = str(self.request.GET.get('receiver', '')) + '-' + self.request.auth.openid
|
||||
if ListModel.objects.filter(sender=receiver, receiver=sender, read=False).exists():
|
||||
ListModel.objects.filter(sender=receiver, receiver=sender, read=False).update(read=True)
|
||||
return ListModel.objects.filter(Q(sender=sender, receiver=receiver) | Q(sender=receiver, receiver=sender))
|
||||
else:
|
||||
return ListModel.objects.none()
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action in ['list']:
|
||||
return serializers.ChatGetSerializer
|
||||
else:
|
||||
return self.http_method_not_allowed(request=self.request)
|
||||
|
||||
class ReadAPI(viewsets.ModelViewSet):
|
||||
"""
|
||||
list:
|
||||
Response a data list(all)
|
||||
"""
|
||||
pagination_class = MyPageNumberPagination
|
||||
filter_backends = [DjangoFilterBackend, OrderingFilter, ]
|
||||
ordering_fields = ['id', "create_time", "update_time", ]
|
||||
filter_class = Filter
|
||||
|
||||
def get_queryset(self):
|
||||
if self.request.user:
|
||||
sender = str(self.request.GET.get('sender', '')) + '-' + self.request.auth.openid
|
||||
return ListModel.objects.filter(receiver=sender, read=False)
|
||||
else:
|
||||
return ListModel.objects.none()
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action in ['list']:
|
||||
return serializers.ChatGetSerializer
|
||||
else:
|
||||
return self.http_method_not_allowed(request=self.request)
|
||||
@@ -1,4 +0,0 @@
|
||||
from django.contrib import admin
|
||||
from . models import ListModel
|
||||
|
||||
admin.site.register(ListModel)
|
||||
@@ -1,4 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
class CompanyConfig(AppConfig):
|
||||
name = 'company'
|
||||
@@ -1,18 +0,0 @@
|
||||
from django_filters import FilterSet
|
||||
from .models import ListModel
|
||||
|
||||
class Filter(FilterSet):
|
||||
class Meta:
|
||||
model = ListModel
|
||||
fields = {
|
||||
"id": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"company_name": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"company_city": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"company_address": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"company_contact": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"company_manager": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"creater": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"is_delete": ['exact', 'iexact'],
|
||||
"create_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range'],
|
||||
"update_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range']
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
class ListModel(models.Model):
|
||||
company_name = models.CharField(max_length=255, verbose_name="Company Name")
|
||||
company_city = models.CharField(max_length=255, verbose_name="Company City")
|
||||
company_address = models.CharField(max_length=255, verbose_name="Company Address")
|
||||
company_contact = models.BigIntegerField(default=0, verbose_name="Company Contact")
|
||||
company_manager = models.CharField(max_length=255, verbose_name="Company Manager")
|
||||
creater = models.CharField(max_length=255, verbose_name="Who Created")
|
||||
openid = models.CharField(max_length=255, verbose_name="Openid")
|
||||
is_delete = models.BooleanField(default=False, verbose_name='Delete Label')
|
||||
create_time = models.DateTimeField(auto_now_add=True, verbose_name="Create Time")
|
||||
update_time = models.DateTimeField(auto_now=True, blank=True, null=True, verbose_name="Update Time")
|
||||
|
||||
class Meta:
|
||||
db_table = 'company'
|
||||
verbose_name = 'data id'
|
||||
verbose_name_plural = "data id"
|
||||
ordering = ['company_name']
|
||||
|
||||
def __str__(self):
|
||||
return self.pk
|
||||
@@ -1,54 +0,0 @@
|
||||
from rest_framework import serializers
|
||||
from .models import ListModel
|
||||
from utils import datasolve
|
||||
|
||||
class CompanyGetSerializer(serializers.ModelSerializer):
|
||||
company_name = serializers.CharField(read_only=True, required=False)
|
||||
company_city = serializers.CharField(read_only=True, required=False)
|
||||
company_address = serializers.CharField(read_only=True, required=False)
|
||||
company_contact = serializers.IntegerField(read_only=True, required=False)
|
||||
company_manager = serializers.CharField(read_only=True, required=False)
|
||||
creater = serializers.CharField(read_only=True, required=False)
|
||||
create_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
update_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M:%S')
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id']
|
||||
|
||||
class CompanyPostSerializer(serializers.ModelSerializer):
|
||||
openid = serializers.CharField(read_only=False, required=False, validators=[datasolve.openid_validate])
|
||||
company_name = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
company_city = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
company_address = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
company_contact = serializers.IntegerField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
company_manager = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class CompanyUpdateSerializer(serializers.ModelSerializer):
|
||||
company_name = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
company_city = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
company_address = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
company_contact = serializers.IntegerField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
company_manager = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
creater = serializers.CharField(read_only=False, required=True, validators=[datasolve.data_validate])
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
|
||||
class CompanyPartialUpdateSerializer(serializers.ModelSerializer):
|
||||
company_name = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
company_city = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
company_address = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
company_contact = serializers.IntegerField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
company_manager = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
creater = serializers.CharField(read_only=False, required=False, validators=[datasolve.data_validate])
|
||||
class Meta:
|
||||
model = ListModel
|
||||
exclude = ['openid', 'is_delete', ]
|
||||
read_only_fields = ['id', 'create_time', 'update_time', ]
|
||||
@@ -1,12 +0,0 @@
|
||||
from django.urls import path, re_path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path(r'', views.APIViewSet.as_view({"get": "list", "post": "create"}), name="company"),
|
||||
re_path(r'^(?P<pk>\d+)/$', views.APIViewSet.as_view({
|
||||
'get': 'retrieve',
|
||||
'put': 'update',
|
||||
'patch': 'partial_update',
|
||||
'delete': 'destroy'
|
||||
}), name="company_1")
|
||||
]
|
||||
@@ -1,114 +0,0 @@
|
||||
from rest_framework import viewsets
|
||||
from .models import ListModel
|
||||
from . import serializers
|
||||
from utils.page import MyPageNumberPagination
|
||||
from rest_framework.filters import OrderingFilter
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework.response import Response
|
||||
from .filter import Filter
|
||||
from rest_framework.exceptions import APIException
|
||||
|
||||
class APIViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
retrieve:
|
||||
Response a data list(get)
|
||||
|
||||
list:
|
||||
Response a data list(all)
|
||||
|
||||
create:
|
||||
Create a data line(post)
|
||||
|
||||
delete:
|
||||
Delete a data line(delete)
|
||||
|
||||
partial_update:
|
||||
Partial_update a data(patch:partial_update)
|
||||
|
||||
update:
|
||||
Update a data(put:update)
|
||||
"""
|
||||
pagination_class = MyPageNumberPagination
|
||||
filter_backends = [DjangoFilterBackend, OrderingFilter, ]
|
||||
ordering_fields = ['id', "create_time", "update_time", ]
|
||||
filter_class = Filter
|
||||
|
||||
def get_project(self):
|
||||
try:
|
||||
id = self.kwargs.get('pk')
|
||||
return id
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_queryset(self):
|
||||
id = self.get_project()
|
||||
if self.request.user:
|
||||
if id is None:
|
||||
return ListModel.objects.filter(openid=self.request.auth.openid, is_delete=False)
|
||||
else:
|
||||
return ListModel.objects.filter(openid=self.request.auth.openid, id=id, is_delete=False)
|
||||
else:
|
||||
return ListModel.objects.none()
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action in ['list', 'retrieve', 'destroy']:
|
||||
return serializers.CompanyGetSerializer
|
||||
elif self.action in ['create']:
|
||||
return serializers.CompanyPostSerializer
|
||||
elif self.action in ['update']:
|
||||
return serializers.CompanyUpdateSerializer
|
||||
elif self.action in ['partial_update']:
|
||||
return serializers.CompanyPartialUpdateSerializer
|
||||
else:
|
||||
return self.http_method_not_allowed(request=self.request)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
data = self.request.data
|
||||
data['openid'] = self.request.auth.openid
|
||||
if ListModel.objects.filter(openid=data['openid'], company_name=data['company_name'], is_delete=False).exists():
|
||||
raise APIException({"detail": "Data exists"})
|
||||
else:
|
||||
if ListModel.objects.filter(openid=data['openid'], is_delete=False).count() >= 1:
|
||||
raise APIException({"detail": "You Just Can Create 1 Company"})
|
||||
else:
|
||||
serializer = self.get_serializer(data=data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=200, headers=headers)
|
||||
|
||||
def update(self, request, pk):
|
||||
qs = self.get_object()
|
||||
if qs.openid != self.request.auth.openid:
|
||||
raise APIException({"detail": "Cannot update data which not yours"})
|
||||
else:
|
||||
data = self.request.data
|
||||
serializer = self.get_serializer(qs, data=data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=200, headers=headers)
|
||||
|
||||
def partial_update(self, request, pk):
|
||||
qs = self.get_object()
|
||||
if qs.openid != self.request.auth.openid:
|
||||
raise APIException({"detail": "Cannot partial_update data which not yours"})
|
||||
else:
|
||||
data = self.request.data
|
||||
serializer = self.get_serializer(qs, data=data, partial=True)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
serializer.save()
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=200, headers=headers)
|
||||
|
||||
def destroy(self, request, pk):
|
||||
qs = self.get_object()
|
||||
if qs.openid != self.request.auth.openid:
|
||||
raise APIException({"detail": "Cannot delete data which not yours"})
|
||||
else:
|
||||
qs.is_delete = True
|
||||
qs.save()
|
||||
serializer = self.get_serializer(qs, many=False)
|
||||
headers = self.get_success_headers(serializer.data)
|
||||
return Response(serializer.data, status=200, headers=headers)
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
from django.contrib import admin
|
||||
from . models import ListModel
|
||||
|
||||
admin.site.register(ListModel)
|
||||
@@ -1,4 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
class CustomerConfig(AppConfig):
|
||||
name = 'customer'
|
||||
@@ -1,49 +0,0 @@
|
||||
from rest_framework_csv.renderers import CSVStreamingRenderer
|
||||
|
||||
def file_headers():
|
||||
return [
|
||||
'customer_name',
|
||||
'customer_city',
|
||||
'customer_address',
|
||||
'customer_contact',
|
||||
'customer_manager',
|
||||
'customer_level',
|
||||
'creater',
|
||||
'create_time',
|
||||
'update_time'
|
||||
]
|
||||
|
||||
def cn_data_header():
|
||||
return dict([
|
||||
('customer_name', u'客户名称'),
|
||||
('customer_city', u'客户城市'),
|
||||
('customer_address', u'详细地址'),
|
||||
('customer_contact', u'联系电话'),
|
||||
('customer_manager', u'负责人'),
|
||||
('customer_level', u'客户等级'),
|
||||
('creater', u'创建人'),
|
||||
('create_time', u'创建时间'),
|
||||
('update_time', u'更新时间'),
|
||||
])
|
||||
|
||||
def en_data_header():
|
||||
return dict([
|
||||
('customer_name', u'Customer Name'),
|
||||
('customer_city', u'Customer City'),
|
||||
('customer_address', u'Customer Address'),
|
||||
('customer_contact', u'Customer Contact'),
|
||||
('customer_manager', u'Customer Manager'),
|
||||
('customer_level', u'Customer Level'),
|
||||
('creater', u'Creater'),
|
||||
('create_time', u'Create Time'),
|
||||
('update_time', u'Update Time'),
|
||||
])
|
||||
|
||||
|
||||
class FileRenderCN(CSVStreamingRenderer):
|
||||
header = file_headers()
|
||||
labels = cn_data_header()
|
||||
|
||||
class FileRenderEN(CSVStreamingRenderer):
|
||||
header = file_headers()
|
||||
labels = en_data_header()
|
||||
@@ -1,20 +0,0 @@
|
||||
from django_filters import FilterSet
|
||||
from .models import ListModel
|
||||
|
||||
class Filter(FilterSet):
|
||||
class Meta:
|
||||
model = ListModel
|
||||
fields = {
|
||||
"id": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"customer_name": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"customer_city": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"customer_address": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"customer_contact": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"customer_manager": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"customer_level": ['exact', 'iexact', 'gt', 'gte', 'lt', 'lte', 'isnull', 'in', 'range'],
|
||||
"creater": ['exact', 'iexact', 'contains', 'icontains'],
|
||||
"is_delete": ['exact', 'iexact'],
|
||||
"create_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range'],
|
||||
"update_time": ['year', 'month', 'day', 'week_day', 'gt', 'gte', 'lt', 'lte', 'range']
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user