From 546c70cbcd0aa99e82ce038944e89bc05a723aab Mon Sep 17 00:00:00 2001 From: Macbook Date: Sun, 21 Dec 2025 03:45:02 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B7=D0=B0=D0=BA=D0=B0=D0=B7=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/routers/__init__.py | 74 ++- .../__pycache__/__init__.cpython-313.pyc | Bin 9681 -> 10872 bytes .../__pycache__/orders.cpython-313.pyc | Bin 0 -> 2623 bytes api/routers/orders.py | 42 ++ api/static/css/layout.css | 1 + api/static/js/index.js | 628 +++++++++++++++++- db/__pycache__/initialize.cpython-313.pyc | Bin 13778 -> 13778 bytes .../__pycache__/orders.cpython-313.pyc | Bin 0 -> 7506 bytes .../__pycache__/toolkit.cpython-313.pyc | Bin 18932 -> 19249 bytes db/handlers/orders.py | 98 +++ db/handlers/toolkit.py | 13 + db/schemas/__init__.py | 14 +- .../__pycache__/__init__.cpython-313.pyc | Bin 356 -> 444 bytes db/schemas/__pycache__/orders.cpython-313.pyc | Bin 0 -> 2144 bytes db/schemas/orders.py | 30 + 15 files changed, 862 insertions(+), 38 deletions(-) create mode 100644 api/routers/__pycache__/orders.cpython-313.pyc create mode 100644 api/routers/orders.py create mode 100644 db/handlers/__pycache__/orders.cpython-313.pyc create mode 100644 db/handlers/orders.py create mode 100644 db/schemas/__pycache__/orders.cpython-313.pyc create mode 100644 db/schemas/orders.py diff --git a/api/routers/__init__.py b/api/routers/__init__.py index deaeac6..039138e 100644 --- a/api/routers/__init__.py +++ b/api/routers/__init__.py @@ -1,15 +1,17 @@ -from datetime import date, datetime, timedelta +from datetime import datetime, timedelta from fastapi import APIRouter, Depends, Request from fastapi.responses import RedirectResponse from db.handlers.access import AccessLevelHandler from db.handlers.categories import CategoryHandler +from db.handlers.orders import OrdersHandler from utils import render, requestDict, logger from .user import router as user from .stocks import router as stocks from .toolbox import router as toolbox from .toolkit import router as toolkit from .records import router as records +from .orders import router as orders router = APIRouter() @@ -19,6 +21,7 @@ router.include_router(stocks, prefix="/stocks", tags=["stocks"]) router.include_router(toolbox, prefix="/toolbox", tags=["toolbox"]) router.include_router(toolkit, prefix="/toolkit", tags=["toolkit"]) router.include_router(records, prefix="/records", tags=["records"]) +router.include_router(orders, prefix="/orders", tags=["orders"]) @router.get("/") @@ -52,6 +55,21 @@ async def post_requests( from db.handlers.toolkit import ToolkitHandler from db.handlers.user import UserHandler + def getDates(data: dict): + startDate = data.get("startDate") + if isinstance(startDate, str): + startDate = datetime.strptime(startDate, "%Y-%m-%d").date() + if startDate is None: + startDate = datetime.now().date() - timedelta(days=30) + endDate = data.get("endDate") + if isinstance(endDate, str): + endDate = datetime.strptime(endDate, "%Y-%m-%d").date() + if endDate is None: + endDate = datetime.now().date() + if startDate > endDate: + startDate, endDate = endDate, startDate + return startDate, endDate + reqData = { "tab": request_data.get("body").get("tabId"), } @@ -105,18 +123,7 @@ async def post_requests( "categories": categories, } case "jurnal_toolkits": - startDate = request_data.get("body").get( - "startDate", date.today() - timedelta(days=7) - ) - if isinstance(startDate, str): - startDate = datetime.strptime(startDate, "%Y-%m-%d").date() - - endDate = request_data.get("body").get("endDate", date.today()) - if isinstance(endDate, str): - endDate = datetime.strptime(endDate, "%Y-%m-%d").date() - - if startDate > endDate: - startDate, endDate = endDate, startDate + startDate, endDate = getDates(request_data.get("body")) jurnal_toolkits = await StocksRecordsHandler.getLogs(startDate, endDate) if isinstance(jurnal_toolkits, list): @@ -147,18 +154,7 @@ async def post_requests( "endDate": endDate.strftime("%Y-%m-%d"), } case "jurnal_service": - startDate = request_data.get("body").get( - "startDate", date.today() - timedelta(days=7) - ) - if isinstance(startDate, str): - startDate = datetime.strptime(startDate, "%Y-%m-%d").date() - - endDate = request_data.get("body").get("endDate", date.today()) - if isinstance(endDate, str): - endDate = datetime.strptime(endDate, "%Y-%m-%d").date() - - if startDate > endDate: - startDate, endDate = endDate, startDate + startDate, endDate = getDates(request_data.get("body")) jurnal_service = await ServiceRecordsHandler.getLogs(startDate, endDate) if isinstance(jurnal_service, list): @@ -199,7 +195,33 @@ async def post_requests( "users": users, "accessLevels": accessLevels, } - # logger.info(resultData) + case "orders": + fullAccess = reqData["accessData"].get("view_all_toolboxes", False) + startDate, endDate = getDates(request_data.get("body")) + if fullAccess: + result = await OrdersHandler.get_all(startDate, endDate) + if "errorMessage" in result.keys(): + resultData["message"] = result["errorMessage"] + else: + resultData["status"] = "ok" + if "orders" in result.keys(): + resultData["data"]["orders"] = result["orders"] + else: + userId = reqData.get("userData").get("id") + result = await OrdersHandler.get_all_by_consumer( + userId, startDate, endDate + ) + if "errorMessage" in result.keys(): + resultData["message"] = result["errorMessage"] + else: + resultData["status"] = "ok" + if "orders" in result.keys(): + resultData["data"]["orders"] = result["orders"] + if resultData["status"] == "ok": + resultData["data"]["startDate"] = startDate.strftime("%Y-%m-%d") + resultData["data"]["endDate"] = endDate.strftime("%Y-%m-%d") + resultData["data"]["users"] = await UserHandler.getAll() + resultData["data"]["fullAccess"] = fullAccess case _: pass return resultData diff --git a/api/routers/__pycache__/__init__.cpython-313.pyc b/api/routers/__pycache__/__init__.cpython-313.pyc index 3eb2c8610faf529a6b2bbe04ce0c3c3d67d673b2..2f92d941b4c78bf2f49a3847bbbea42668f639c2 100644 GIT binary patch literal 10872 zcmd5iZEzdMb$h_!gZLo6L3|Jd_yLe0DS)C#QD2lui59-D!Q%`3yl$FMD z|FmxxI0B?-$&%epSK_<3d%JJnd;9k7ySKPyHX9I>=WF|?_Cyf+4X!9fTSRVoR0w?w zF^Hjt(GX(M5XDj_4XYt->S67Wj@1#I9@Y<4uoVQ?3>$`wtdZc_ zVbhSAH4|JnTsdT6Ekjn;I%H#Q(57c9hV4TR)?UP&fIEx03vgEvcLVM&;#GiG6>$&X zo)9{wu0x$g5m?7D$LJPCu+sCGoiQ-RIK`N@t6{{(eVmM$sk}=c7h_?pcj@D1Y>fRb zeX1A-U3JJz;Gfxol8a4az*b$gQMA* zT$+`2ed#ml%mgRvM$=Euq`6#_lC7iZi77Tco*PYbXR;YC4Q=+`@$odr9Y~)|PY#zpl@nCCu^s(lap!oBdigiqh$#< zPix{c9CW2I1opLO*!0P%r(`;pn&h6=#kpK|{0UCh7M@S*Vl(oet6~`}${Nu$%BKpVM?IQf$vO?=|O+5<1rc0nv@^URW ztzjre6<1}bF}3n0p@C&m!vg6Olw;NKl_hK{c1t-B8q=(fLCX6<*v_Yb)>-OX>Jg0z zH6To7z>H8vl~5hl?1Ui`;07KvB9u^NV6z;gjldy;GU|kSm3jcE>lEq?&1e|yc2$N> z(4!SfYw4L#GdjH4@zzVw4I7MXKzcON-=#iDMGYgesi^oQS+`21mLkToMwM78c(>nb z1+be^R0#qB8(G_76&B|#`WARBED#oC5|L@Bk-tM=w5nK}tbQVWUe=9gvrkNcF=CRT z&;@yOJOTE?#lbAu^!a#S`mACY@zj~Ac)_-~crrPanaU-Tu`}o8id~cGO!}!a>~6XG zbZRP-Jd>JC$97F;$5YeX?%3+vAWXr*`k+64gf5`GA^yE1^W$@kf4Td6M?U3fXuoK4 zQcoL+*~Y=9VTKr(3fQiChMks;TrQQH;gaLoiL{JZSS)PqI+z&t7Z=sAbG3BHOmR837S~O%lhe8MKGI3n11+*o2{{fMZ3veh zww?@L6_AZX-4B1an4NR?5+mVj+I z3}+(VB!JVynjvk5Cb(u;H>A(tb5c8I2P&k1U)5p1n$a?P*zTCCb%m)=w&{j%4a(a# zDx+hJccX!AKlLg?jOlJYH=NC=%tq+6gFCn%o>!@CIEP7@BcWqVjleHs{$jKIFY;^zNn@+m1(Jb)Sb# z`B}YJ`Q314JLBlYo&(CNQ}@Cet4HxxURRVjVEy5dsW{jLBj7MtUp_;E)%D(xC#RwT z<$3#jiHcQj!NW7Igr0HZwRm)%(c))9-+(|nRaiUyR4RPm`tg+X(@iOJPJo}hPknn! z^fHDork#v2q1s4)kN!wm(cAQ9N@(tBUCsAs2bE*^Od};>a-fGToGOku6`aFx%w_+3 zBW@X=vdRys9t2;A&t^`Qu_&z>D+Cju@AYL2A3vldR^wJ>%x!Yp#=ruKy-j2ITqH%& zld`TU@bM*EN|+MnL}kLp_+vEKSGiqPlU>IUE1>U5qqeb#GW#e#eb<%J7tC!k*b}x@ zKAm9U_SG5$ZDm~B5_TpKQ?28;rnnQj2+!KOyLxd&mowlUGLDQBY%RKuS*Ru)38$jP z+cB$6@u?gp$-!q|@wp>I1 zhx@|y|7l;8*^R4U?O(QL!3)=tb^P6ZTOM`Kmgg?BJog84ts!Rv>r7%2(6(m{ClH%3 zk)l672R>BCnDH0-hNoj$e3tPh?#FL?SI-HoJ&50n@^mOz=?SO8o&33vX-xP=;RIP< zl(>*Y-4Kr@peL3HG|+^jv4ekG(gqXx95is!!N)|3G_?51L0jL@Ih`@&k+(}@BgYRk_1)jwH=#gI&xIo~a6W9h& zJ@O`jf4`=;dgL@|r2(o&zC+-%1a<L`QV7wvWf-ukaT4g(?{HKdH{6|D3GTZc@BGHw+1-1?&I>kO5k#v$G+ zqlYs}>q;qhy%Vi5h_MTWQ8V;*Y7^W?0R4@n)~rgcDocE-zM#~q&nYz|LdW4NL(-1LzDm{uc7|l*v5CQ(O!gd$ts|;tvCC$0 z{-5mzP)u!x3JE+tki^tEId~?^<&wor7+1qibFJpd1H9l6B0eqtDfj$bm^$z=@C5-@SX<*Pyg(oWOVYzYUuE?>90)RsT4ge zyr)I*Y7j;WM z!M#g#_ww#u!M$&`UozUC-vjSon*PT0iy6@!<=s)i-GW~@1!Ij=+cbAhvUu{A+PuE@ z7Ki|O(Ol|;OrOY3oR>9#4o=9%V(J&Pk&^W|=S(m|A>T}JO`2u1|62Zs`8V>H^FIvbU&}86`Ud}X{;Rn1I{!i_{}1^eEV)16WH($|%w#5%Wr9?0=HwK6T6RsO zPeA@z$zv+b222sWQF3Y`O0)QCLDpc&IJmPZcrI9YeX$eALbe3b%j6*&#=$D!`UR5V zvgPp^Hj^sw2h=zg+XCc|F+uiW3?9J%4=>r7Tv;3i^I?|?8R=60BWt=68Za>?C7TKg zC$~W`^{EthDm?)?bdEa*H!7@$^dSrJAmr&GwJz&U7m%{K4JT)&rwi9g(SYojI1xKl zxLD$1#fusiheGTYOl{5UqFz`WAKm^2q-U{Pl&$3?u}WEN2C!w}28!JcC|gPDQ<;-l zSqpopcY0bjz;o~UgXc1E52c1oJZWIy+KWw1%k(r(!SBIC@g1$KgU$!Clbo!cPG`ve zK1ues`b_o=3sDwgvCAgiu<^(=++T3A14a>BI&mHw2RCyXZoB*}j$=y_6XA#)glo2s z5CY>VSUjZsXQr`NNEHmUVmy`UOLJ3`8Ci=(9h@jzNwV_fBEhKG(cnbEl-PBvd=xV= zhJg)(V;KAi0DO&yXXJ3sk=!n93<@60yv9$l=V1)w2oQHwhtNmhoIcSb%|p~Rx}E%Q zU7^jIx^L!wkKnNqLKT&>e=1snyd}7x6)cUSrG>Y&EcOeQjuo}aqP@_+qD6Ywyh-#l z@t&qdx8P|LbZwH+HD4`yqr5k|7!kaiMQ4rn-wkWEXbth!(1KgAHi_0& z-rBnOm|)#0TKDkQJy#5Zb$CUM%+9$XxT}B(OrH5&qQ8arw=C`#{98nS7w_-7Y!>|c z1>^p^Q8NkF2Mg2=@TOXz7HC);QWmz@5WhA#~-7zAgF=#2Bu_!2ERJ8!8~wf0Yt%5R_3{XvVY zRZ?TioMC<&Z*7pmt@B%XYu!p8jZE%5?fPUNjVe8oC-}1Q+s1_hf@ibn>Eu0~lC^0u z41~OuK*)QLx?%IYDeL$*d6>GNeHCu$5 zPMCjoaM>4lTQAfcdPjfxk(ZzO_A`sS-VbhjThG@V5`3ex!&0C{3~c2CTVKmv9u~R= zgutL!H8?x4Z1K)Fz0xOIqP!(qlKMudNea|Swarqf>1Kr{Vh3#yEj+F+XyFe@HP!;A z+Rg{sg+PZ`)dA#!F)`SAHP|TxyF_o-?9g>f)!z)QrCZl}Q==|XJY1ys$%8;K_(5%S zu~lr|!8h-?yj5u4E7b1eO*ONFbLVa}Y!e%L_=cV<-naUNhQngb;n{!om`1V}h0hP(K1*JXnc-Y3;Ji}hRh z`YlUCLj9hbW?i!z#+>Dyk(IBg3UaQTR-yWsSl7YVbqIA^#p=Yv1 zVyJs=aJer0!QMlHXY`-rOXptpf7`!s?0s+hPvX316bz!?JvR)i4THkF`S9)^=dKJ3 zdma+Phs5Baxq)&%w@9^^L#d(lbMtw;$mbsps!(Ok9kf@!O|0+X>kHbeKP(0g&yHa2 z#a?X?Epgrw$J+af;D{{N3XYa?u88&`O?PC;s4c0kM^W9A)NQI;Zrmz1c3*Am78>`6 zwR`4<$|q{a;z!y(_e76VMO%D~LY1!Dwg{Fk{EAbwM0iUCOE>f4js@F`d&;H+vuJ7t zYg}dimL3J_=K4s8_>F;lAojj9ep3x?zYTZ6@?8z@7Q#JZuxHLxX49}y8rs&_^xh(8 z|G5)Yx+QODp_=!`L~lFqZO?D%f6FC!56%vK_F`SPc$b5%VzA?Cuw&`S6`c?q5WNF) zTFFtn@bC}DzB~5)#{@^a)Y`tZn{V9GkRmy;H9eI!qy+`{O->0cMDZ*Qdjro zQ+!wdykTK0?`dA##(O&cc*7eac6wCswu#;j-rKQsSn%$c(~^06t_FJ~r*D36;T+g+ z2zy4KeNGET?5z887?7~r;VtzGIle!gSB&@mv!29#G_ytPHz(ydf?@zyrrn)q$-x|J5mWQEfZ zy!giqjr`j_rorWZ>_(nJN_NI%Pv@s{%^|sJ@M9);`%)0h-4nMbFn{1}u)KkYJ^qUo3 zfd3Rx$D{O5sl9%vyj@KlZ>8T3RAIb>Iv%Ir?!@>zChB-Q{f^m;ai8yar~aLe2Ec#j zp^opMf9CC_q4I8kI^IpcTT_kk9aIw0@9ymNK;`ESDygP_?(A)aN*+;3EuE(@*}R8J zR?vAbCYz7=k|sTmFj>BVO4?{X+}i>b!Ad2av|uBZ_jXaqD*C-{JjXA+RMJQP($`xF zmG_&dWPpA@+S>#b$wnnZv}Es%Lgj;D!=Nck`ek>yZ2h4a@|ZNo9OG6 zUW`Za>+7uq#tCdEuoElnIt**3J_ObQstME*SV5o(_xaFC@G1g*1O^BU5m--Pgh1f6 znHq@($pK#wSct-qY%8`yuwD4|2v_*|iM>2(F2wmHl$G7( zFG^n?g-CeKsKT$R3bC(dCO0+B6@K0V+22CUT?j6%xa!0Je`B$345~07-w%~jnepkF ziFC5?`<+6-j(@-+(>S_h)3_&&ELnV^$?nGhQUUNKw(E3uVrDwMn|%(dI8(#<0j{Vi z3KZS+A&T=TehtO`6>YwT+OMJ3578zbZMud+*HFVX6jA;++|(k2dWJObyWi$Dv|*d8^k4Hm>zmzo zF}R|#Qv=leBexKK-clo#t8AQg73GGn_umr)_qX3v J;WnbY{{eKb*9QOq literal 9681 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 diff --git a/api/routers/__pycache__/orders.cpython-313.pyc b/api/routers/__pycache__/orders.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..64492da243607bd6d30eafc53531d26753903d29 GIT binary patch literal 2623 zcmbVOO>7fK6rR~#uQ##RiIad!Qi5@up9N&6knkfy)j(Q8Qk=?Lqf`k(PP_z@#2aR9 zXv)Qe13^7NQBjSkN{*oR0Ig6_t0EyTJ+3j5-Bl}8l?yjVp_S;VZ+2}bAktDs+L`y} z{mkF{-rFTntVYm;&i(NlHiZ75Ot}GXwxz#exQt{Z<3Til2_9ewUzwyJSv;Q6XGkVpa??*NrV!wlywmSA(`^g<6SR)*fYiXp}G8c43(x zb#^hby%8Pfj^oOz;wpXwxk|3y#jN7R@q!oEgi{iq&^<_uQPumwfv}oND#Wn&DKknU zrb*Zkhe%8zT7NVVn^Fj!x zk%yTISs|A1^8noZF|a?0FS8+=6Zw&VLfj7aD3)+YVhDXULl{?6iDXDQbptI?A4V`3 z(O{QB*dQ+b5hnJJ=2^Zo?GX`SFxA-(&od49bEK9lhN_gTZ0@S9zOH* z0XXTaonFS}7l&wLWVRD0a0G?RF>1QCy5&fDsnk~XlVD!Uz7lx}_OymVllchnR@i#*QI0LhsjXVWCVdk3KZwD07j@Zic%ewrVN(cEiNTGjv01}z2Ka%P z!KVNYFxtn}>1idAG@Pc|K}FM|6N+J*3@!$NEJ1>U^$jkbII0>fpu|EP zpPB)&Vz6;=#!V`-8fex~O@S!djGBP;8h|BhXy>NgS}Ns1nwm;#)T!5Mgx<6v&Zyc7 z1h&8&MWN;ZBvAxD6_CwwZg6f;uif^L>nhZ~k!ja!eChszYt4np^OKpjoNHs=)tYs+ zF2r-L&h-9**l=Ow{77bPPW0wQe^&G_4Cll^`n}Z!tGW_uvO?1WpY?7n2(GNqn3>2n zzxCMT&wDmM@NE8O-%b11@84eEAuHK|3Uz)7g3fNamcK>c)DX|IWzmmdm zU3aPMya@jl1;!R4emFg$cWwrt{c+L;B3g~yOs6A%^x zD6ln1Sv2UafUkhR>f!2X{^Im!rx&*8Je%^K zK-Lq`gS|P=UY%?BmDVfY1Gf)^?w-7(N9TI7j-F>)9jq|d+u6Gd-`k3NcX0Q%*?{kw?|_pEjlW zB99p1T;rL>`F&?MEHW5xT4E97E>fx}P+8NNrg=Gs-M=?9Wd?H%9gA!|ZYYTEr*x#s ubwviK;+C@VA$|S9bd!!lzxX#_AI$l8E+P)^$$a>f4%A=>FET)yd;SO9;2-D! literal 0 HcmV?d00001 diff --git a/api/routers/orders.py b/api/routers/orders.py new file mode 100644 index 0000000..d8ebb46 --- /dev/null +++ b/api/routers/orders.py @@ -0,0 +1,42 @@ +from fastapi import APIRouter, Depends +from db.handlers.orders import OrdersHandler +from utils import requestDict, logger + + +router = APIRouter() + + +@router.get("/") +async def get_requests(): + return await OrdersHandler.countNew() + + +@router.post("/") +async def post_requests( + reqData: dict = Depends(requestDict), +): + logger.info("Обновление заказа") + response = {"status": "error"} + orderId = reqData.get("body").get("orderId") + userId = reqData.get("body").get("userId") + status = reqData.get("body").get("status") + comment = reqData.get("body").get("comment") + if (orderId is not None and userId is not None) and ( + status is not None or comment is not None + ): + result = await OrdersHandler.update(int(orderId), userId, status, comment) + if "errorMessage" in result.keys(): + response["message"] = result["errorMessage"] + else: + response["status"] = "ok" + if "orders" in result.keys(): + response["data"] = result["orders"] + return response + + +@router.post("/new", summary="Добавление нового заказа") +async def add_order(reqData: dict = Depends(requestDict)): + logger.info(f"Добавление нового заказа") + userId = reqData.get("body").get("userId") + customer_comment = reqData.get("body").get("customer_comment") + return await OrdersHandler.new(userId, customer_comment) diff --git a/api/static/css/layout.css b/api/static/css/layout.css index f090e7c..5f86c77 100644 --- a/api/static/css/layout.css +++ b/api/static/css/layout.css @@ -2,6 +2,7 @@ body { background-image: url("../images/background.svg"); background-repeat: repeat; background-size: 512px auto; + background-attachment: fixed; } .loader-bg { diff --git a/api/static/js/index.js b/api/static/js/index.js index 803ce47..10ea46a 100644 --- a/api/static/js/index.js +++ b/api/static/js/index.js @@ -89,7 +89,11 @@ async function checkActiveUser() { } } -async function openTab(event, tabId) { +async function openTab(event, tabId, autoLoad = false) { + const activeTab = loadFromStorage('tab'); + if (activeTab && activeTab.tabId === tabId && !autoLoad) { + return; + } // Убираем активный класс со всех вкладок и кнопок document.querySelectorAll('.tab-nav-btn').forEach(btn => { btn.classList.remove('active'); @@ -136,6 +140,12 @@ function prepareTabs() { }; } + tabsData['orders'] = { + title: 'Заказы', + icon: 'bi-basket', + description: 'Управление заказами' + }; + if (accessData.view_requests) { tabsData['jurnal_toolkits'] = { title: 'Журнал перемещений', @@ -170,11 +180,11 @@ function prepareTabs() {
+ + + +
+
+
+ + + + +
+
+ ${!fullAccess ? ` +
+ +
+ ` : ` +
+
+ + + + +
+
+ ` } +
+ + + + +
+ +
+ + +
+
+
+ + Дата начала: + + +
+
+ + Дата окончания: + + +
+
+
+ + + + `; + + // Модальное окно для нового заказа + if (!fullAccess) { + document.body.insertAdjacentHTML('beforeend', ` + + `); + } + + + + // Инициализация фильтров + if (fullAccess) { + document.getElementById(`${tabId}-customer-filter`).value = currentFilters.customer; + } + document.getElementById(`${tabId}-status-filter`).value = currentFilters.status; + document.getElementById(`${tabId}-search-filter`).value = currentFilters.search; + + // Обработчики событий + const filterResetBtn = document.getElementById(`${tabId}-filter-reset-btn`); + filterResetBtn.addEventListener('click', () => { + currentFilters = { + customer: 'all', + status: 'all', + search: '' + }; + if (fullAccess) { + document.getElementById(`${tabId}-customer-filter`).value = currentFilters.customer; + } + document.getElementById(`${tabId}-status-filter`).value = currentFilters.status; + document.getElementById(`${tabId}-search-filter`).value = currentFilters.search; + saveToStorage(tabId, currentFilters); + renderOrdersTable(); + }); + + // Дата фильтры + const startDateInput = document.getElementById(`${tabId}-date-from`); + const endDateInput = document.getElementById(`${tabId}-date-to`); + startDateInput.value = startDate; + endDateInput.value = endDate; + + const refreshDateBtn = document.getElementById(`${tabId}-date-update-btn`); + refreshDateBtn.addEventListener('click', async () => { + const newStartDate = startDateInput.value; + const newEndDate = endDateInput.value; + if (newStartDate && newEndDate) { + tabContent.innerHTML = ` + + `; + + const cookiesData = { userData, accessData }; + const newPeriodData = await apiRequest('/', { + tabId: tabId, + startDate: newStartDate, + endDate: newEndDate, + cookiesData + }); + if (newPeriodData.status === 'ok') { + renderOrdersTab(tabId, { + ...tabData, + ...newPeriodData.data, + startDate: newStartDate, + endDate: newEndDate, + fullAccess + }); + await checkNewOrders(); + } + } + }); + + // Кнопка нового заказа + if (!fullAccess) { + const newOrderBtn = document.getElementById(`${tabId}-new-order-btn`); + newOrderBtn.addEventListener('click', () => { + const modal = new bootstrap.Modal(document.getElementById(`${tabId}-new-order-modal`)); + modal.show(); + }); + + // Отправка нового заказа + const orderSubmitBtn = document.getElementById(`${tabId}-order-submit-btn`); + orderSubmitBtn.onclick = async () => { + if (orderSubmitBtn.disabled) return; + + orderSubmitBtn.disabled = true; + + const description = document.getElementById(`${tabId}-order-description`).value; + if (!description.trim()) { + showInfo('Пожалуйста, введите описание заказа', 'warning'); + orderSubmitBtn.disabled = false; + return; + } + + try { + const result = await apiRequest('/orders/new', { + userId: userData.id, + customer_comment: description + }); + + if (result.status === 'ok') { + const modal = bootstrap.Modal.getInstance( + document.getElementById(`${tabId}-new-order-modal`) + ); + modal.hide(); + document.getElementById(`${tabId}-order-description`).value = ''; + refreshDateBtn.click(); + } else { + showInfo(result.message || 'Ошибка при создании заказа', 'danger'); + } + } finally { + orderSubmitBtn.disabled = false; + } + }; + } + + + if (orders.length === 0) { + tabContent.innerHTML = ` + + `; + return; + } + + // Рендерим таблицу + tabContent.innerHTML = ` +
+
+
+ + + + + + + + + ${fullAccess ? '' : ''} + + + + + +
ЗаказчикОписание заказаСтатусИсполнительКомментарий исполнителя⚙️
+
+ +
+
+ `; + + // Функция фильтрации + function filterOrders() { + let filtered = orders; + + // Фильтр по заказчику + if (fullAccess && currentFilters.customer !== 'all') { + filtered = filtered.filter(o => o.customer_id == currentFilters.customer); + } + + // Фильтр по статусу + if (currentFilters.status !== 'all') { + filtered = filtered.filter(o => o.status === currentFilters.status); + } + + // Поиск + if (currentFilters.search.trim()) { + const searchTerm = currentFilters.search.toLowerCase(); + filtered = filtered.filter(o => { + return ( + (userMap[o.customer_id] || '').toLowerCase().includes(searchTerm) || + o.customer_comment.toLowerCase().includes(searchTerm) || + (userMap[o.executor_id] || '').toLowerCase().includes(searchTerm) || + (o.executor_comment || '').toLowerCase().includes(searchTerm) || + o.status.toLowerCase().includes(searchTerm) + ); + }); + } + + return filtered; + } + + // Функция рендеринга таблицы + function renderOrdersTable() { + const tbody = document.getElementById(`${tabId}-orders-body`); + const noOrdersDiv = document.getElementById(`${tabId}-no-orders`); + const filteredOrders = filterOrders(); + + if (filteredOrders.length === 0) { + tbody.innerHTML = ''; + noOrdersDiv.style.display = 'block'; + return; + } + + noOrdersDiv.style.display = 'none'; + + tbody.innerHTML = filteredOrders.map(order => { + // Определяем класс для статуса + const statusClass = { + new: 'warning', + working: 'primary', + complete: 'success', + cancelled: 'danger' + }[order.status] || 'secondary'; + + // Определяем русское название статуса + const statusText = { + new: 'Новый', + working: 'В работе', + complete: 'Выполнен', + cancelled: 'Отменен' + }[order.status] || order.status; + + // Рендерим статус + let statusCell; + if (fullAccess && (order.status === 'new' || order.status === 'working')) { + statusCell = ` + + `; + } else { + statusCell = `${statusText}`; + } + + // Рендерим комментарий исполнителя + let commentCell; + if (fullAccess && (order.status === 'new' || order.status === 'working')) { + commentCell = ` + + `; + } else { + commentCell = `${order.executor_comment || 'Нет комментария'}`; + } + + return ` + + + ${userMap[order.customer_id] || `Пользователь ${order.customer_id}`}
+ ${order.created_at} + + ${order.customer_comment} + + ${statusCell}
+ ${order.updated_at} + + ${order.executor_id ? (userMap[order.executor_id] || `Пользователь ${order.executor_id}`) : '-'} + ${commentCell} + ${fullAccess ? ` + + ${fullAccess && (order.status === 'new' || order.status === 'working') ? ` + + ` : '☑️'} + + `: ''} + + `; + }).join(''); + + function updateRowState(orderId) { + const original = originalOrders[orderId]; + const current = changedOrders[orderId]; + + const hasChanges = + current && + ( + (current.status !== undefined && current.status !== original.status) || + (current.executor_comment !== undefined && current.executor_comment !== original.executor_comment) + ); + + const saveBtn = document.querySelector( + `.save-row-btn[data-order-id="${orderId}"]` + ); + + if (saveBtn) { + saveBtn.disabled = !hasChanges; + } + } + + // Добавляем обработчики изменений + if (fullAccess) { + document.querySelectorAll(`.order-status`).forEach(select => { + select.addEventListener('change', function () { + const orderId = this.dataset.orderId; + + changedOrders[orderId] = { + ...changedOrders[orderId], + status: this.value + }; + + updateRowState(orderId); + }); + }); + + document.querySelectorAll(`.executor-comment`).forEach(textarea => { + textarea.addEventListener('input', function () { + const orderId = this.dataset.orderId; + + changedOrders[orderId] = { + ...changedOrders[orderId], + executor_comment: this.value + }; + + updateRowState(orderId); + }); + }); + document.querySelectorAll('.save-row-btn').forEach(btn => { + btn.addEventListener('click', async function () { + const orderId = this.dataset.orderId; + const original = originalOrders[orderId]; + const current = changedOrders[orderId]; + + if (!current) return; + + // Формируем diff + const payload = { orderId, userId: userData.id }; + + if (current.status !== undefined && current.status !== original.status) { + payload.status = current.status; + } + + if ( + current.executor_comment !== undefined && + current.executor_comment !== original.executor_comment + ) { + payload.comment = current.executor_comment; + } + + if (Object.keys(payload).length === 2) return; + + this.disabled = true; + + const result = await apiRequest('/orders/', payload); + + if (result.status === 'ok') { + // Обновляем оригинал + originalOrders[orderId] = { + status: payload.status ?? original.status, + executor_comment: payload.comment ?? original.executor_comment + }; + + delete changedOrders[orderId]; + + if (payload.status && original.status === 'new') { + await checkNewOrders(); + } + + this.innerHTML = ''; + setTimeout(() => { + this.innerHTML = ''; + }, 1500); + + if (originalOrders[orderId].status === 'complete' || originalOrders[orderId].status === 'cancelled') { + const foundOrder = orders.find(order => order.id === Number(orderId)); + if (foundOrder) { + foundOrder.executor_id = userData.id; + foundOrder.executor_comment = originalOrders[orderId].executor_comment; + foundOrder.status = current.status; + } + renderOrdersTable(); + } + } else { + showInfo('Ошибка сохранения', 'danger'); + this.disabled = false; + } + }); + }); + } + } + + // Обработчики фильтров + if (fullAccess) { + document.getElementById(`${tabId}-customer-filter`).addEventListener('change', function () { + currentFilters.customer = this.value; + saveToStorage(tabId, currentFilters); + renderOrdersTable(); + }); + } + + document.getElementById(`${tabId}-status-filter`).addEventListener('change', function () { + currentFilters.status = this.value; + saveToStorage(tabId, currentFilters); + renderOrdersTable(); + }); + + document.getElementById(`${tabId}-search-filter`).addEventListener('input', function () { + currentFilters.search = this.value; + saveToStorage(tabId, currentFilters); + renderOrdersTable(); + }); + + // Первоначальный рендеринг + renderOrdersTable(); +} + function renderJurnalToolkitsTab(tabId, tabData) { const tabContent = document.getElementById(`${tabId}-tab-content`); const tabOptionalContent = document.getElementById(`${tabId}-tab-optional-content`); @@ -7446,6 +8031,34 @@ function renderUsersTab(tabId, tabData) { renderUsersCards(); } +async function checkNewOrders() { + const result = await apiRequest('/orders/', {}, 'GET'); + + if (result && result.orders > 0) { + const ordersTabBtn = document.getElementById('orders-tab'); + + // Удаляем старый бейдж, если есть + const oldBadge = ordersTabBtn.querySelector('.orders-badge'); + if (oldBadge) oldBadge.remove(); + + const newOrdersBadge = document.createElement('span'); + newOrdersBadge.className = + 'badge rounded-pill bg-danger position-absolute orders-badge'; + newOrdersBadge.textContent = result.orders; + + // Позиция: правый верх + newOrdersBadge.style.top = '6px'; + newOrdersBadge.style.right = '8px'; + + ordersTabBtn.appendChild(newOrdersBadge); + } + if (result && result.orders == 0) { + const ordersTabBtn = document.getElementById('orders-tab'); + const oldBadge = ordersTabBtn.querySelector('.orders-badge'); + if (oldBadge) oldBadge.remove(); + } +} + document.addEventListener('DOMContentLoaded', async () => { await getCookieData(); @@ -7456,6 +8069,7 @@ document.addEventListener('DOMContentLoaded', async () => { } prepareTabs(); + await checkNewOrders(); }); window.openTab = openTab; \ No newline at end of file diff --git a/db/__pycache__/initialize.cpython-313.pyc b/db/__pycache__/initialize.cpython-313.pyc index e3e3afdbb304b37b85e3e72254fb6622f9c403be..4b288ab621de1ad70e312623ec6d3cd251e2b4f0 100644 GIT binary patch delta 20 acmcbVeJPv!GcPX}0}zzSxo_k?ZVCWN2?j6# delta 20 acmcbVeJPv!GcPX}0}y!rblb>%+!O#z>jvEb diff --git a/db/handlers/__pycache__/orders.cpython-313.pyc b/db/handlers/__pycache__/orders.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..08700dfd6034fed8d2380fb02e460cdacdcd1440 GIT binary patch literal 7506 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 JOfc6b`Cs*KH*5d^ literal 0 HcmV?d00001 diff --git a/db/handlers/__pycache__/toolkit.cpython-313.pyc b/db/handlers/__pycache__/toolkit.cpython-313.pyc index 2a75d4420da41ef174647485bc54e9428f6020c0..e54d0d7840b83914e46afe8f15986e9f422f4ff8 100644 GIT binary patch delta 1072 zcmZ9KO=uHA6vt;~ceeSQG-*ECfMAK_5K5^UwG>n^LTXVjY2MyYNOobH= z2MGL%{$lx6p)1~KbsxUN&swE~Y!j1IsU%e-=#q)!gfrCJcmQT!)f9+3A;^%r%nKm zm=V+9Fs7UY$I-#~QBU0@)Wx7riODHX5QSjIo^ZHz9MiF_W5dQcq)eDoutU;wJ`Qn%K=!G3G`6ORRb<;Pdy-gSqJ(V4 zW$#loAU2R4(?<7wnf`x}1BYQeJ+nCRv=^e=}Gw~Yv^%3vgo%=w=TVzF- zmFFLF@eWx9K9Avye~<4O;w!R6m+dd@`^6t*mz@1R_d4gd7)>=!6;Fmko3(4T^7`#k z?dF{ujg8`?`U5z+MGknh&*pfhSyKe0y*@y(G-AXQa%EE6`gy#au?UEhfm2C43JfC< zCjzIYA?h=oU#6{$BB&ilcWVQDuT!TQGZnILI?J=of<-`_44gX0?5i{=b0}IBuulYe zyeTaL;$+}d+K#ZQb)}toc2yDdo&2ueU9xNs#dTb8>?V!p+Rz)s?UJ*b^jVTuXiHNB z75^YqrwFZDu=_>%;Z{Kr5Zn~xrDmZxmEcs?-eiUGh^2Ax7HjM+f22E&_Jz}tTbxFI K5qwthGyMlvoAI## delta 776 zcmY+9O=uHA6vt;~ck;2xPCmNb#;70$T3f0V2~rSI+DdFdJxo0aOQ2RMjlsei=tYR9 z;KAEk33!ROy%a5b5ibh%=E<8#1d9j?f}3RH+4r_dz&X7Cd%yY5dvEri)4-vkuj?9t zGs8<;KbQ8!2c;3k8czQkgk540n@VCcNo_?QlZvHE)#61}@;s@kRcc3M1ZpOuSliKI z6$`7F%*(jNkx9*p7b7w;e5Hk5m`sMggw*rIPNJ^?twacQnX-h%IjkYmkg1r#;7oX% zlG$+VL|CO^H5_uZnt=>f%Z!+uw6ZYV480R_3WganXHRaj+2dct+V*zs{#O0k`h&Z* zdtQl)m^cBW9u^rPzsO_q+1t<;xn4&02m&%^)?D#^XQvc#3-Rs{cf5?bz{EN-^rN-o z-ON!w&SFn^w}$(JL593hqo`rT`F@;Cf*{uGiJT{Ln_`O zD~;LVxhGs4Agj{R20O)ri0B|&9JAGnpW@;Vvg^Z-e)Y~=Z8B;)uSyr3e(7oBBiuGg zm(BA+tL_sJcVmF!TQtrMw-68z-_(wBkZw-pH)y-y6V%s3eN!KVwV%Aqr(4Sb0qHF< z1uVXn;6_UZ1jNrHX|K73m$!YQaHqB!TL~aRo5JT@cnQ+gOkSmJ#V4q^5vsqC;iuZm iJ^{hGAn%m`Wiai~@^`xD{3u^aRX9!c2uAxP{O%tyBf&}l diff --git a/db/handlers/orders.py b/db/handlers/orders.py new file mode 100644 index 0000000..d0e9e1b --- /dev/null +++ b/db/handlers/orders.py @@ -0,0 +1,98 @@ +from datetime import date, datetime, time +from sqlalchemy import func, select +from db import CRUD +from db.schemas.orders import Orders +from utils.loggers import logger + + +class OrdersHandler: + @staticmethod + async def new(user_id: int, order: str): + try: + await Orders(customer_id=user_id, customer_comment=order).save() + except Exception as e: + logger.error(f"Ошибка создания заказа: {str(e)}") + return {"errorMessage": f"Ошибка создания заказа: {str(e)}"} + return {"status": "ok"} + + @staticmethod + async def get_all_by_consumer( + user_id: int, startDate: date, endDate: date, toDict: bool = True + ): + try: + startDate = datetime.combine(startDate, time.min) + endDate = datetime.combine(endDate, time.max) + query = ( + select(Orders) + .where( + Orders.customer_id == user_id, + Orders.created_at.between(startDate, endDate), + ) + .order_by(Orders.created_at.asc()) + ) + orders = await CRUD.read(query, True) + ordresList = [order.toDict() for order in orders] if toDict else orders + return {"orders": ordresList} + except Exception as e: + logger.error(f"Ошибка получения заказов: {str(e)}") + return {"errorMessage": f"Ошибка получения заказов: {str(e)}"} + + @staticmethod + async def get_all(startDate: date, endDate: date, toDict: bool = True): + try: + startDate = datetime.combine(startDate, time.min) + endDate = datetime.combine(endDate, time.max) + query = ( + select(Orders) + .where( + Orders.created_at.between(startDate, endDate), + ) + .order_by(Orders.created_at.asc()) + ) + orders = await CRUD.read(query, True) + ordresList = [order.toDict() for order in orders] if toDict else orders + return {"orders": ordresList} + except Exception as e: + logger.error(f"Ошибка получения заказов: {str(e)}") + return {"errorMessage": f"Ошибка получения заказов: {str(e)}"} + + @staticmethod + async def countNew(): + try: + query = select(func.count(Orders.id)).where(Orders.status == "new") + return {"orders": await CRUD.read(query)} + except Exception as e: + logger.error(f"Ошибка получения количества заказов: {str(e)}") + return {"errorMessage": f"Ошибка получения количества заказов: {str(e)}"} + + @staticmethod + async def get(order_id: int, toDict: bool = False): + try: + order = await CRUD.read(select(Orders).where(Orders.id == order_id)) + return order.toDict() if toDict else order + except Exception as e: + logger.error(f"Ошибка получения заказа: {str(e)}") + return {"errorMessage": f"Ошибка получения заказа: {str(e)}"} + + @staticmethod + async def update( + order_id: int, user_id: int, status: str = None, comment: str = None + ): + try: + if status is None and comment is None: + logger.error("Не указан статус и комментарий") + return {"errorMessage": "Не указан статус и комментарий"} + order = await OrdersHandler.get(order_id) + if not order or isinstance(order, dict): + logger.error("Заказ не найден") + return {"errorMessage": "Заказ не найден"} + changeData = {"executor_id": user_id} + if status: + changeData["status"] = status + if comment: + changeData["executor_comment"] = comment + await order.edit(**changeData) + except Exception as e: + logger.error(f"Ошибка обновления заказа: {str(e)}") + return {"errorMessage": f"Ошибка обновления заказа: {str(e)}"} + return {"status": "ok"} diff --git a/db/handlers/toolkit.py b/db/handlers/toolkit.py index a3e6d72..7d395c9 100644 --- a/db/handlers/toolkit.py +++ b/db/handlers/toolkit.py @@ -25,6 +25,8 @@ def handleToolkitImage(imageData, title: str): class ToolkitHandler: + + @staticmethod async def add(toolkitData: dict, user_id: int = None): title = toolkitData.get("title", None) if not title: @@ -71,6 +73,7 @@ class ToolkitHandler: await ServiceRecordsHandler.add(user_id, {"Добавлен инструмент": toolkitData}) return newToolkit.toDict() + @staticmethod async def updateMovindDate(toolkitId: int): toolkit = await CRUD.read(select(Toolkit).where(Toolkit.id == toolkitId)) if not toolkit: @@ -82,6 +85,7 @@ class ToolkitHandler: return False return True + @staticmethod async def updateRefillDate(toolkitId: int): logger.info(f"Обновление даты пополнения инструмента {toolkitId}...") toolkit = await CRUD.read(select(Toolkit).where(Toolkit.id == toolkitId)) @@ -94,12 +98,14 @@ class ToolkitHandler: return False return True + @staticmethod async def hideToolkit(userId: int, toolkitId: int, hidden: bool = True): logger.info( f"{'Скрытие' if hidden else 'Отображение'} инструмента {toolkitId}..." ) return await ToolkitHandler.edit(userId, id=toolkitId, hidden=hidden) + @staticmethod async def edit(user_id: int, **kwargs): title = kwargs.get("title", None) toolkitId = kwargs.pop("id") @@ -165,11 +171,13 @@ class ToolkitHandler: ) return editedToolkit.toDict() + @staticmethod async def getAll(): query = select(Toolkit) toolkits = await CRUD.read(query, True) return [toolkit.toDict() for toolkit in toolkits] if toolkits else [] + @staticmethod async def get(toolkitId: int): query = select(Toolkit).where(Toolkit.id == toolkitId) toolkit = await CRUD.read(query) @@ -183,16 +191,19 @@ class ToolkitHandler: logger.info(data) return data + @staticmethod async def getSeveral(toolkitIds: list[int]) -> list[dict]: query = select(Toolkit).where(Toolkit.id.in_(toolkitIds)) toolkits = await CRUD.read(query, True) return [toolkit.toDict() for toolkit in toolkits] if toolkits else [] + @staticmethod async def checkCatogoryUse(category_id: int): query = select(Toolkit).where(Toolkit.category_id == category_id) toolkit = await CRUD.read(query) return True if toolkit else False + @staticmethod async def delete(toolkitId: int, user_id: int = None): movements = await StockHandler.checkToolkitExists(toolkitId) if movements: @@ -217,6 +228,7 @@ class ToolkitHandler: ) return {"status": "ok"} if result else {"errorMessage": "Инструмент не удален"} + @staticmethod async def addComment(toolkitId: int, user_id: int, comment: str): logger.info(f"Добавление комментария к инструменту {toolkitId}...") logger.info(f"Комментарий: {comment}") @@ -236,6 +248,7 @@ class ToolkitHandler: logger.info(f"Комментарий к инструменту {toolkit.title} успешно добавлен") return {"status": "ok"} + @staticmethod async def initialize(): from .categories import CategoryHandler diff --git a/db/schemas/__init__.py b/db/schemas/__init__.py index adefaaf..bcfcb81 100644 --- a/db/schemas/__init__.py +++ b/db/schemas/__init__.py @@ -4,13 +4,17 @@ from .toolkit import * from .categories import * from .toolbox import * from .stock import * - +from .records import * +from .orders import * __all__ = [ "User", - "Access", - "Toolbox", - "Category", - "Stock", + "AccessLevel", "Toolkit", + "Category", + "Toolbox", + "Stock", + "StocksRecords", + "ServicesRecords", + "Orders", ] diff --git a/db/schemas/__pycache__/__init__.cpython-313.pyc b/db/schemas/__pycache__/__init__.cpython-313.pyc index 651e7d68437a099c7667323ef2a953206d448cc4..364a38c320573ac157ac774d97466cb64a194899 100644 GIT binary patch delta 226 zcmaFDw1=7RGcPX}0}#xWbI%NCoX97^7&B4bu|Aj~n9-ZfoJoNJ%wh+z^cjMg0vU@q z(wQ|`UNQppXfobn)Y9a<#S&VaT6BxsF*!N4xY#GPEH&pAdq{qMPIhL=Ee_|zlGOD4 zqDqKRQhvoP*5H!-O!xW(a|SdyBaUsQREHMk@{IU6FAomt|i$v*L` i>11n0Z!Qs_F(9LgF#YA*`IP| zrclGfMiYbjATeny(I_T9w21~EeZ(hUyCD&{Vu1+^@8Dsb8O)HTS%Cpd(^;OD3NpwA1(bpcY9R!n zf(BY43}N4|OzVX%=qf}Y;_K>k6rz1>h#8>)W@rnMaeN;wh%Kv`@IyLgB%zlrm%DbA zXEdRmsaL#eP3U>F&7w_2ChNfM<=Qk~6XCNpmoIY=n&E<7TNdgpzwBmYp`MwU%M10i z??t8lD;FU4EOr5HEm5Q}k;gdqC@k?k4m-&+ARj>z;q=)MDnzuszEVa?|i=!?? z@z*M%SZ8z6%xMyXNwaf3^uRP*W?NDb3kcaR zuR0>^aMyBO$f!bfcx6dw7cN<_>@ccL&m6k6s`BYV4!J z)id{FJ=gLZ-Y>BuI|@6HX;Fnw0Yl}{uQi5Zoz0^R)=Zmp9i8?eHpe=o9LDCNiAwie zyW$AVt>^8MyCuN@+8x3-4U`fP%|yJNaS%mU9RwNky`M;adgbFQ8~NLbL)-cxIE2=x zQlyda0sdN3C=Mg$&y%Jh8S)AFEDnwHSl+6%t`tKtHq3NZRF5l5QbsAx5_^Cz0e8+L zduS#J4M~UGfg`BwDgm2mt#T1<*FcJ2{zh`sZT9BwBy!t&u9?XF z?ZhzP{r(~83n{IB85)xQRoEvO(0Uie^A$F)VyM67*i2`%bWFJC|jg)K;q5JMqIL ziaUvkZGEDdm}o;Wa!mS8I;wsjIwozgVrGCSU9+kjDIGD*YQ5}L=o~Z6i=I_!dlII( zWP{^Y>>97t(H%EUmxef_qsq5zQ?2o@17d5&AJaxm?(i1N$4gfoQ{=py&tC+c`O-~1%7Z5En?-?Qfckr3!Z!MRhvBq9M%%BJOtNB|+7;YA7?CNV-HOBZ@glOAH1 zFaZB)EP5_?OcRb$0FgcTNT>mR7pM^DI*Je3-Q-~7-0xk9YvCKSKPH=9ry8g4?d#h} zeKWH8_U)n9ZtpwMnBHx;m22*sLR0GCMtO6tnL4(uCP`UBTBdTO#Cxty-gvzkA8Ab9 z>rQRxn=juS-5PCnA8!

9_iR9{g#r**r zi8vQ4R)yx(TF?z2E^0*37Xmi`UM?^C!~>efaF~Q&`O|0!{bay}Q*atXXqGr>6n`p` gB>lz?K467Atnh&K|H@*2X({RC)!9c3H$Ss~061#qE&u=k literal 0 HcmV?d00001 diff --git a/db/schemas/orders.py b/db/schemas/orders.py new file mode 100644 index 0000000..d32d87e --- /dev/null +++ b/db/schemas/orders.py @@ -0,0 +1,30 @@ +from datetime import datetime +from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Text +from db import CRUD, Base +import utils + + +class Orders(Base): + __tablename__ = "orders" + + id = Column(Integer, primary_key=True, autoincrement=True) + customer_id = Column(Integer, ForeignKey("users.id")) + executor_id = Column(Integer, ForeignKey("users.id"), nullable=True) + customer_comment = Column(Text, nullable=False) + executor_comment = Column(String, nullable=True) + status = Column(String, default="new") + created_at = Column(DateTime, default=datetime.now) + updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) + + def __init__(self, **kwargs): + for key, value in kwargs.items(): + setattr(self, key, value) + + def toDict(self): + return utils.toDict(self) + + async def save(self): + return await CRUD.create(self, refresh=True) + + async def edit(self, **kwargs): + return await CRUD.update(Orders, self.id, **kwargs)