From b4a8192c654e4daf6c24c61768713db4c2cfb038 Mon Sep 17 00:00:00 2001 From: emile Date: Tue, 9 Apr 2024 23:27:52 +0300 Subject: [PATCH] new --- .../billing/__pycache__/admin.cpython-310.pyc | Bin 453 -> 472 bytes .../__pycache__/models.cpython-310.pyc | Bin 3362 -> 4025 bytes osinaweb/billing/admin.py | 10 +- osinaweb/billing/migrations/0015_order.py | 24 ++++ .../migrations/0016_alter_order_status.py | 18 +++ .../billing/migrations/0017_service_order.py | 19 +++ .../migrations/0018_alter_order_status.py | 18 +++ ...ayment_remove_service_customer_and_more.py | 111 ++++++++++++++++++ osinaweb/billing/migrations/0020_orderitem.py | 22 ++++ ...21_remove_item_recurring_cycle_and_more.py | 23 ++++ .../__pycache__/0015_order.cpython-310.pyc | Bin 0 -> 1111 bytes .../0016_alter_order_status.cpython-310.pyc | Bin 0 -> 725 bytes .../0017_service_order.cpython-310.pyc | Bin 0 -> 751 bytes .../0018_alter_order_status.cpython-310.pyc | Bin 0 -> 733 bytes ..._service_customer_and_more.cpython-310.pyc | Bin 0 -> 2358 bytes .../0020_orderitem.cpython-310.pyc | Bin 0 -> 904 bytes ...m_recurring_cycle_and_more.cpython-310.pyc | Bin 0 -> 837 bytes osinaweb/billing/models.py | 70 +++++++---- .../__pycache__/views.cpython-310.pyc | Bin 3632 -> 3864 bytes osinaweb/customercore/views.py | 14 ++- osinaweb/db.sqlite3 | Bin 991232 -> 999424 bytes 21 files changed, 295 insertions(+), 34 deletions(-) create mode 100644 osinaweb/billing/migrations/0015_order.py create mode 100644 osinaweb/billing/migrations/0016_alter_order_status.py create mode 100644 osinaweb/billing/migrations/0017_service_order.py create mode 100644 osinaweb/billing/migrations/0018_alter_order_status.py create mode 100644 osinaweb/billing/migrations/0019_item_orderpayment_remove_service_customer_and_more.py create mode 100644 osinaweb/billing/migrations/0020_orderitem.py create mode 100644 osinaweb/billing/migrations/0021_remove_item_recurring_cycle_and_more.py create mode 100644 osinaweb/billing/migrations/__pycache__/0015_order.cpython-310.pyc create mode 100644 osinaweb/billing/migrations/__pycache__/0016_alter_order_status.cpython-310.pyc create mode 100644 osinaweb/billing/migrations/__pycache__/0017_service_order.cpython-310.pyc create mode 100644 osinaweb/billing/migrations/__pycache__/0018_alter_order_status.cpython-310.pyc create mode 100644 osinaweb/billing/migrations/__pycache__/0019_item_orderpayment_remove_service_customer_and_more.cpython-310.pyc create mode 100644 osinaweb/billing/migrations/__pycache__/0020_orderitem.cpython-310.pyc create mode 100644 osinaweb/billing/migrations/__pycache__/0021_remove_item_recurring_cycle_and_more.cpython-310.pyc diff --git a/osinaweb/billing/__pycache__/admin.cpython-310.pyc b/osinaweb/billing/__pycache__/admin.cpython-310.pyc index 341638ead60110d5c5d500df683068ecc1920d1f..f8b78a6b95687c3ea02ca4a7799b98c7d4081f34 100644 GIT binary patch delta 150 zcmX@ge1n-cpO=@50SJ~X5l!=+$SccOF;P34Bb70Wc>zl*&%{Y8jJy-K+xfD1mZaw1 zV)ZXdNiDj?38ugz?4EgL`I*V7w|Kx}0g08lsd**0xS@=Y%7WBe>_Mr?shI^OD;bIe cfHoBgOtxh#k`Mthc^Ehtd02QD5m0~;0P$BU!2kdN delta 134 zcmcb?e3Y3tpO=@50SM0OiKZD%f-B8Rl@g{MhAwXe~Q#+NMfT6xgJZ`h^xKia?HJwQg+Ljn-|$90-atlDX!R z+acGE1m$D{Il0GPf&em*L;Wv$>~ENBPCDn7Q=WIomDc(|+ZFg~<{8d#_`J{i&da>h zX&LxUzq-En@3vulO`XM`h0Z#z;$JY_;4C#JEMSb*@}1RFP=H3ADr zliNV-8fgM?xC_Ls5gUldTR>Vh;s9xLABbNgE|3oI0_oO>2c*aQK>9V(0x_yLSC0>+KN6pdr+r4cka@5p4q-qSh;> ztU{!FuMFv8!nfht!$QbnO-zzhh%`+O#M-7P9+&xaZ6`{T$;#bmN}c;fQpWjYEiaNR zTFb;!-QeRW+s`ZTsn}iHO|V3^zgE5d>hwT4VOW$h4C!g#&}Myhc;oeVTCH!{9y-bm z!z`MJFjTED#459t+I|@ReHNwFh@@4NejI?CqD#*Bo z%Y3G>3yz^>*6GH7!pt4RSfFJO zwrLk-Ql>%@eBN8+#_!V{^0UnjeR|z9jog%^blD-(g;Cb%;|I4ZQFt4VqFNDe(U3P1 zpO#u7s?9|a%Vb(6d8S%hxfIEM_Mw14!QoiMrE)fI?QGoIyseyRjkN2b(ZN(Gt0ddn zO7o~*(vBwiEGw1&L7t}~%JhQ*PtF5Yd7DuwDzp_#fesI&GVtRlixF$Or|c*$lPBV8 z_4e^5^tQ!K7}v0vZ~1J1KOcWx{RchMU6<+^xKWH@&g)rJN;aV|tz+I0NT|?tBWOW? z9=DM^kv`FHqwk~bLARvsF4`@v+urBK9^*dmJa>cs=Z0kjOVDUnHSbF<>ut>WkiV=DYdg<`{1rN{Sf$EqXlr2Dkk`>xPW&i`(hDuV z6Aoxp51q+W5@TBss1`1%^fiG}v{P#6;VS+DLjo<{D%sc^H_)~?d%=#IFOAO{rG3Z|VQpx@3>@8ePy3$kw_w{i73f^B0-sy(byInoAj(mfw_#|s-8vV*942UYCnh$CL$~8^f_bleq3hTC`s4_E>5-fH;gWFA$0wVkh7sPhZwT(e$lPC?RX@tY1 zs+D-MpOnS?*wC`2dgKE0Ccq6`y7cDA{#1O=OK-n^=*nI$SB9+8)>RrNy-0io8HdY@ z8$R>M`Al1tJH2~Rb}-7xR%I}yKm8=-Z&oi>;grZLVS8HGI;U#!lXs*R|BzZghQZOA zPe^!I`}rf>_D5U=6=1GGMTiUScgf#0cER7o`8uwG4){fXpXHWgTR1PNq^><(M+0(; zjs^$c3A0Y*?c-BvU=5tXV6Y794(uD=cjYWYivR!s delta 1483 zcmZux&2Jk;6yNo(ch~Fn+S%Gp9e>0jO^cPrBu!gVTEM|+AfgHu2?zGFa(5=F!^Vzg z*ALY|U@qdo%Mse(&x6 zI{o96J;-EC34Xsf3p)#`$96^j=JEX38kyLV8qUuzp2?;LyDeDZFZ@ZWd_)wY9?Pyy zl|6%|08NrSPPl2ITSO5S?O1U$!eR?uAv##HAm@ldQpAKC6QDUHO)T(sL7dG47Qnwk zGRLZ01Z@&z8}=`bWrZAN;c5v~M;y$7Hbo|g^OfY5$puI{AM0C`J?#1P>^I2zRF2L4 z?&n@W`@7MD?S>pD?+4xfXJAha`W~SHjc6mm@^F?F0YJuwFq|nMoh1QI6R}OG-`oGF z8+Zpn*QepgFX`>}qHrDD>4ffdhe6a(go9Y^4}(CsIh9#$s{NqL)_XJ{>@|L0ef?^-Jq)A4K4l#?*y#l{ z*4wLF?bWrrvEh$*VjcGOs2}kYwQ5*vSVTAA&(zCxrb4*b*t)yzZGLj^o||RVA2QbK z?|S|tKcM_C^}~tW7 zgGag%-3Nh2BB8 zNBBf?s(b@16<8tiYDhUC)BrLqncd)jm>vC$fSoxo7FDf|;tc;ZeMu7mXZe@uMePz= z-{PG_wXg!>9IO!E(NTZc3O`C!xnX(N?eP?Kx{vm0KiX!~c=JCtSqW5_cI5Hj$*=LH zbct`s8UEawHezi=7}DZkDJ%_eSRDK1wam&btcTq}c(r~vg@lT_M{HrP>>5H7VUB;7 zxm>*s;ykR-1~4QW2FnXW^S>7>= zFqhDzG806t;^DUuUS_(?Pt5sJ3yf$x$L}AKXNpd+7RH@q?}D|V#rnt8r@e#df1>(! zQxooI`M37fnh3WBw?^TxG*>?uanF|dpY}olM^xnU9v(w&vytWt+3N+7oS+Erv6k59 iKbezrv2kaAK!yQ*M+T!9a7zM`R7Z1iP72U08vg*)9Yw+b diff --git a/osinaweb/billing/admin.py b/osinaweb/billing/admin.py index f5b4f91c..3005339d 100644 --- a/osinaweb/billing/admin.py +++ b/osinaweb/billing/admin.py @@ -3,11 +3,11 @@ from .models import * # Register your models here. - admin.site.register(RecurringCycle) -admin.site.register(Service) -admin.site.register(ServicePayment) -admin.site.register(PaymentType) +admin.site.register(Item) +admin.site.register(Order) +admin.site.register(OrderItem) admin.site.register(Invoice) +admin.site.register(OrderPayment) +admin.site.register(PaymentType) admin.site.register(Receipt) -admin.site.register(Charge) \ No newline at end of file diff --git a/osinaweb/billing/migrations/0015_order.py b/osinaweb/billing/migrations/0015_order.py new file mode 100644 index 00000000..c170f759 --- /dev/null +++ b/osinaweb/billing/migrations/0015_order.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.5 on 2024-04-09 19:06 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('osinacore', '0065_alter_customerprofile_status'), + ('billing', '0014_servicepayment_comment'), + ] + + operations = [ + migrations.CreateModel( + name='Order', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('status', models.CharField(choices=[('Completed', 'Completed'), ('Failed', 'Failed'), ('Cancelled', 'Cancelled'), ('Pending', 'Pending'), ('None', 'None')], default='None', max_length=200)), + ('order_id', models.CharField(blank=True, max_length=100, null=True)), + ('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='osinacore.customerprofile')), + ], + ), + ] diff --git a/osinaweb/billing/migrations/0016_alter_order_status.py b/osinaweb/billing/migrations/0016_alter_order_status.py new file mode 100644 index 00000000..a226d8bc --- /dev/null +++ b/osinaweb/billing/migrations/0016_alter_order_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.5 on 2024-04-09 19:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0015_order'), + ] + + operations = [ + migrations.AlterField( + model_name='order', + name='status', + field=models.CharField(choices=[('Completed', 'Completed'), ('Failed', 'Failed'), ('Cancelled', 'Cancelled'), ('None', 'None')], default='Cancelled', max_length=200), + ), + ] diff --git a/osinaweb/billing/migrations/0017_service_order.py b/osinaweb/billing/migrations/0017_service_order.py new file mode 100644 index 00000000..ce24a3f9 --- /dev/null +++ b/osinaweb/billing/migrations/0017_service_order.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.5 on 2024-04-09 19:10 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0016_alter_order_status'), + ] + + operations = [ + migrations.AddField( + model_name='service', + name='order', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='billing.order'), + ), + ] diff --git a/osinaweb/billing/migrations/0018_alter_order_status.py b/osinaweb/billing/migrations/0018_alter_order_status.py new file mode 100644 index 00000000..9e0b7c5c --- /dev/null +++ b/osinaweb/billing/migrations/0018_alter_order_status.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.5 on 2024-04-09 19:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0017_service_order'), + ] + + operations = [ + migrations.AlterField( + model_name='order', + name='status', + field=models.CharField(choices=[('Completed', 'Completed'), ('Failed', 'Failed'), ('Cancelled', 'Cancelled'), ('None', 'None')], default='None', max_length=200), + ), + ] diff --git a/osinaweb/billing/migrations/0019_item_orderpayment_remove_service_customer_and_more.py b/osinaweb/billing/migrations/0019_item_orderpayment_remove_service_customer_and_more.py new file mode 100644 index 00000000..7043fd64 --- /dev/null +++ b/osinaweb/billing/migrations/0019_item_orderpayment_remove_service_customer_and_more.py @@ -0,0 +1,111 @@ +# Generated by Django 4.2.5 on 2024-04-09 20:21 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('osinacore', '0065_alter_customerprofile_status'), + ('billing', '0018_alter_order_status'), + ] + + operations = [ + migrations.CreateModel( + name='Item', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('description', models.TextField(blank=True)), + ('amount', models.FloatField(null=True)), + ('recurring', models.BooleanField(default=False)), + ('created_at', models.DateField(blank=True, null=True)), + ('cancelled_at', models.DateField(blank=True, null=True)), + ('active', models.BooleanField(default=True, null=True)), + ('project', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to='osinacore.project')), + ('recurring_cycle', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='billing.recurringcycle')), + ('type', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, to='osinacore.projecttype')), + ], + ), + migrations.CreateModel( + name='OrderPayment', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', models.DecimalField(decimal_places=3, max_digits=5)), + ('date_paid', models.DateField()), + ('comment', models.TextField(blank=True, null=True)), + ], + ), + migrations.RemoveField( + model_name='service', + name='customer', + ), + migrations.RemoveField( + model_name='service', + name='order', + ), + migrations.RemoveField( + model_name='service', + name='project', + ), + migrations.RemoveField( + model_name='service', + name='recurring_cycle', + ), + migrations.RemoveField( + model_name='service', + name='type', + ), + migrations.RemoveField( + model_name='servicepayment', + name='charge', + ), + migrations.RemoveField( + model_name='servicepayment', + name='type', + ), + migrations.RemoveField( + model_name='invoice', + name='charge', + ), + migrations.AddField( + model_name='invoice', + name='order', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='billing.order'), + ), + migrations.AddField( + model_name='order', + name='due_date', + field=models.DateField(null=True), + ), + migrations.AddField( + model_name='order', + name='paid', + field=models.BooleanField(default=False, null=True), + ), + migrations.DeleteModel( + name='Charge', + ), + migrations.DeleteModel( + name='Service', + ), + migrations.DeleteModel( + name='ServicePayment', + ), + migrations.AddField( + model_name='orderpayment', + name='order', + field=models.ManyToManyField(null=True, to='billing.order'), + ), + migrations.AddField( + model_name='orderpayment', + name='type', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='billing.paymenttype'), + ), + migrations.AlterField( + model_name='receipt', + name='payment', + field=models.ManyToManyField(to='billing.orderpayment'), + ), + ] diff --git a/osinaweb/billing/migrations/0020_orderitem.py b/osinaweb/billing/migrations/0020_orderitem.py new file mode 100644 index 00000000..704a1a49 --- /dev/null +++ b/osinaweb/billing/migrations/0020_orderitem.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.5 on 2024-04-09 20:23 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0019_item_orderpayment_remove_service_customer_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='OrderItem', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='billing.item')), + ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='billing.order')), + ], + ), + ] diff --git a/osinaweb/billing/migrations/0021_remove_item_recurring_cycle_and_more.py b/osinaweb/billing/migrations/0021_remove_item_recurring_cycle_and_more.py new file mode 100644 index 00000000..4bf3858e --- /dev/null +++ b/osinaweb/billing/migrations/0021_remove_item_recurring_cycle_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.5 on 2024-04-09 20:27 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('billing', '0020_orderitem'), + ] + + operations = [ + migrations.RemoveField( + model_name='item', + name='recurring_cycle', + ), + migrations.AddField( + model_name='orderitem', + name='recurring_cycle', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='billing.recurringcycle'), + ), + ] diff --git a/osinaweb/billing/migrations/__pycache__/0015_order.cpython-310.pyc b/osinaweb/billing/migrations/__pycache__/0015_order.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5aa762ecfbc15bffe4ea668407dbc52989982bf8 GIT binary patch literal 1111 zcmYjQv2xrt5G6q#k9RuRvMg(Cr%B37aXin&sWKTWrzn$2EPF;t!v=%n?ol{_1OxDF zpWCEW;a_l@B7;F5LXa~Eft4(eFkTP*8JMhxFrX)aA*7e_iouAuv@Cld-))+E9dn165sue8>tsDy@4`OZy<{s^#&B@S$}qnL_bBE=H+p&# zEJZ%jR@S1>2Hj`#`InEmP%Ro>Y;3D5G>y?KsgT=Nv>QwQ!=+S8)@A<5eE#SQZqdAu z1vX+^VcqgVSN`n=&kUe(Ou|nmj(vYTjon0STFncCqD62!jggfwTYiCCH=<#~LVBbGOIw zsjkr+5a`U+AD)ifusGM0gLOl|mDs43$|+uPg>~7UyHxvBxP&X{-3Lo0>I-w9kc;b$ zQZBtw>-B^8YR+!BYXv*0Yfha(DLB?@BH}*Dl|%*hbb8yRyvv(&H|Ct$XhX_#H{tx3 zjZog%Bfy5z!n%-X4f9d#EIL)6`o1&NFzR17TpXP(j*h=|W8L6&PyZg6jJEB}KXvvq)yrmBk@}>!++uc&_M+8I zc1BgKKK)TN9z3(MrH05lO|lwac8mVdo_vYR?7E+_4>u^I&U(x{*=Nnx_zOCuX&;hF z0~WC`eitV!JYa|6et17*|Hkjq|2n^E;zs=eGgx-w^m;saPtN+=IlIZ%eC`wTXM9Mp XU(Jbg=~sOjz77f5gv=gG!^7x*G}BBN literal 0 HcmV?d00001 diff --git a/osinaweb/billing/migrations/__pycache__/0016_alter_order_status.cpython-310.pyc b/osinaweb/billing/migrations/__pycache__/0016_alter_order_status.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..828222676fcd229ecad8529753f489024f0db80d GIT binary patch literal 725 zcmYjPF>ll`6t?3emxNYAm0*E^0U_Pe6bYCRDk>@q97DyD#q!1Pay}e8#5t9U5!NpJ z1UCK&zrib0cP1oMo|B^1(tGjuy(fSEUTk-F3xez6w>zufl#pKm+*~n&Lk#x_lO&RA z($b2yQ58}0n#f49uS7DS8}@}&OvWcS>t@#S_#_Kp>%U)2o5pa z6HGuVDoGVd+R%zYyh(xpq?=hoWc;1VM5c0f&SZAZup^T>$`w9;`(Fn|XG^8EGL0_| z4jw<{wwKWRWcs9T5X08ZW9-Y1Y};uVK=wIGBaH&?5Ph5j(hBelU(KN714vYj-D{y^;qP}>k2k4K(DLo5BezX=7_J!@>YU)2oe=poNq(7g_ z_YQhjLW?fZmpX&;6}S(B?aC9ORr}zL=mL1>)KJ^Dv`!gO8aN#{awAM*C-M}Q<<*$W z>wjG0z@KpuZZ?gbPvhS2&ie3 literal 0 HcmV?d00001 diff --git a/osinaweb/billing/migrations/__pycache__/0017_service_order.cpython-310.pyc b/osinaweb/billing/migrations/__pycache__/0017_service_order.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c5cf3379121b9648fee1d858f40e9e80c56c872d GIT binary patch literal 751 zcmYjP!EV$r5VajAn=D-kAyGhZ;F?QPE+AD16$#3LvK+R(tj;qv>AdVk}A^B zf;OyR)N`hSg1;j&ko+r=9B3VW35rm%8A-!mc!s3RjhkxOmR)7Ebx~s^C_7#Fz1xu> zIl#302tW!dNx>vt(jov}v*ta8NoSL}g!}U0v zJ(^caReI?j?Cm{!E=tuwD@-d9ZC7?{>qZvZ^J)Pu?90cWC)*=g&r|NgxhnOU<9e-> zON&z;jm8xo<9+fLkKH92Vm?k&2(q*pUqF)SC zhvEgy^Q}es&7aND-xp%*<=}9(+O+-wjWF#!gqW~^U-6JpMjulCH@@ok)5s;mfK1MN s2L|1b{ui_1)nwO#+g&fr-+d!-SUtg-i(d|2zrrU?Y=m%^#dJ6L2hSwefjN5Vhlc(;~DIT;RY7X)jHYkl=t&QBk?Ta;UiEVtM1)tQRLv<1D4(2-n{D z1>E>2{03h+^%o!^p)yX2T1zwWdo%L$yx8pS<^`O?jmA~?XX zPcQ)~sU&3}X-!K8;U@9|5O0#2$nZOrk&I<>&SZMdup^Zj%H=M5`(Fn|lcmyHwY8h= z?>~OZE%YB%1>E!!dKXQf*bZXY+Ifim*`aAV4FkwN!z&;Y9|@%qdS}K4(N>^6b8BM4 zg=wMRK`^F^pPptesn!OitV<-U#8wYzfcV5UwDqvYdOljfThV|E#~iH`Xt|hYF6AT7 zxyw1nzO4q_?{I#y725ZDB%y;=LR%@YeSo4af2jxPkNhb;^F(&I787>4>EO!!AEdwU zZ|l>a&&7K*#TL-ulsK0@KeVb%Dk|>uJa%IE+9 literal 0 HcmV?d00001 diff --git a/osinaweb/billing/migrations/__pycache__/0019_item_orderpayment_remove_service_customer_and_more.cpython-310.pyc b/osinaweb/billing/migrations/__pycache__/0019_item_orderpayment_remove_service_customer_and_more.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fdb7ceccc9393856090f367999b10aa003768406 GIT binary patch literal 2358 zcmbsq%W~UPRJxWWKa@w4IB7x)EpOA-PJxydN~dYkG9CIFlFl&R+);E+T)BFfD><#b zg{{AU4Ge7f4rUm>;ci*w1K2RU&b^Wyw`GSlx<2psxk~2dY6kuGerO#0ykHo=DP;C% z5psjxv_%IPV1`CycFc%%n5l3UmO9pF29&`1#sCY=Uis@%rwnY@XqJyi$7nKHi-NB3 zvLK05X-5fQnBMmlZ}zuN$PIeaFPUMW(J{g3Fff62S*L_nudIBa-E*K!j|~n~#?}FY zD$y!XgZkJc%q6T!{5g%y6ILT^L1T-A)k*V&#+C?kwXNkbBh8f;@P?LFzE(AVjqo`r z<9u(SXZIF+C*UNkk4vPvk#lBvOYjPu($SrMN$b^It6PWH#!TmR=0EdNu`k0}cpcu* z{@+CVJNJ8o{JljSm17xn20o^rpVAv} z9-1%Yvji8QH7-GWta{WSpPMA1>Pj`Ap)+`N2=dtrux`-3z~A{2d*90VcqH1o1SO7hNr5QahA?W}EXUc6FBCjv+(myy<; z3?s<`*t;#+ot>s7U2l*j+!yF&2(sQ6LF9=ce}Y3rQ5hbZ=w6nX4LD9c%p zg?RKU?L*U~{qdgh5aVw4Sk?hkUj%)%O~fkihhF?d+Fq0l;*1DZJP1Qs73dFyph$`2 z03LXQFq5?+2<~M~CYH&#XwV^D--~?=Lj>ZGEk6qmktEip5)M_OhkiCXF)diDAT>+c z5|uLMwAq-UWLY8?rV(&|=!ZC3ElSs#GBmwu9S0&#DTk+3NLDuNr& z9csv7;G?WeMu3de^p&L>BhIMc%DUs7`9ip#JC?Tp*b`lpP7og^WMniqNms{(vjKQ+Gf+trJUV7zRr9B6iG{0yC@=|)@e_wlB4BG5Cv0J zP&(8Eq#=?{K^?73#GW%`>TcX`ZpaGP1>juPIH!UPXf3#OIsaqP@R$P&7iwQ8*y9pPF7WZ1-OULvK*}44im3{-J3}gq8ArJJ5-yA z5Up*{jX$SFvZ3e2-9$3jrxL0ur1g-__SWw9*3K3#_Ca~2X3iVBQ=uLm2qhhcUGp)ZZo0%(Y-prpxQ#~8zDSFxO z#9dU}MYBd!_9x+0W@;_#|NO6-)?ZdxS*T4Rw}7mFEEU(x$;=6lna^uhe$+WNZQ5x) y2QApw%TF6qHppbyMKAPG8 z?yrczHf=qpfKK9qlQ3`>ZsMVzQAO}D$hzRuuLA*eFa0@#khl<|td|8@KN~;2QlxLa28?o4- zZuV_y)7z)6#iD#qodl4jA_8RcjshxXYZ})$qH|V;Jc%qQLQHYQt>CU zK*v~!9FhEAVZ4X>w3bzTLdF!EAJWQ#V=d~LEMloenZ_laH;4ApmORI4eB(rXH*)c0 z@?z4yqcw3{t(2YWE^WO|A#|AMU;7>AK4Kg0hRc5Yf5Ph~XB3#Iy?Ve$`Xkn5}=;Vtd*o&V;X8?_axwbU@)D3*B|^F9RM7i2wiq literal 0 HcmV?d00001 diff --git a/osinaweb/billing/migrations/__pycache__/0021_remove_item_recurring_cycle_and_more.cpython-310.pyc b/osinaweb/billing/migrations/__pycache__/0021_remove_item_recurring_cycle_and_more.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..46f07592349579e76dbea2f5889c93d5cfaab5fd GIT binary patch literal 837 zcmY*XOOMkq5VjpBO|lQTu;O*#+Do%=Mre_s94L##?p|`S(#~v^tP>ZfWmT?l?Y}@s z{3TyG@fScm#>u|0Q;>%sj@U5#K{0G7Tw0fNTP#b{v@WU*pl%Om{_N)PJPv5K zV*+G32bK%qbDjq%E6V6=Yy{a6gb?7MiXnmslPawS)mF6)1Bf>gl1(rd`3|JSV9O;q5G!_Q52(xPoMtV@kZ>a8(*f$iDSEV83Q zm-VAbvGC5@wp`9kotd_5imbu&&giT5o98&qE(FW2KSV~Yc&u#q3gz2C_J3VaMFS*= z#nEDI{dXMDZ0{4qOa$_;3|LX1yBbW5`M=SeiiwK?kl;^mePu_-WslXWTQ02yTm*?*;z=&u#Ig literal 0 HcmV?d00001 diff --git a/osinaweb/billing/models.py b/osinaweb/billing/models.py index 78c80e16..12065f13 100644 --- a/osinaweb/billing/models.py +++ b/osinaweb/billing/models.py @@ -9,29 +9,58 @@ class RecurringCycle(models.Model): return f"{self.months} months" -class Service(models.Model): + +class Item(models.Model): + TYPE = ( + ('Product', 'Product'), + ('Service', 'Service'), + ) title = models.CharField(max_length=200) description = models.TextField(blank=True) - customer = models.ForeignKey(CustomerProfile, on_delete=models.CASCADE) project = models.ForeignKey(Project, blank=True, on_delete=models.CASCADE) type = models.ForeignKey(ProjectType, on_delete=models.CASCADE, blank=True) - amount = models.DecimalField(decimal_places=3, max_digits = 5) + amount = models.FloatField(null=True) recurring = models.BooleanField(default=False) - recurring_cycle = models.ForeignKey(RecurringCycle, on_delete=models.SET_NULL, null=True, blank=True) - created_at = models.DateField(null=True) + created_at = models.DateField(null=True, blank=True) cancelled_at = models.DateField(blank=True, null=True) active = models.BooleanField(default=True, null=True) def __str__(self): return self.title - -class Charge(models.Model): - service = models.ForeignKey(Service, on_delete=models.CASCADE) - amount = models.DecimalField(decimal_places=3, max_digits = 5, null=True) - due_date = models.DateField() - is_paid = models.BooleanField(default=False) + +class Order(models.Model): + STATUS = ( + ('Completed', 'Completed'), + ('Failed', 'Failed'), + ('Cancelled', 'Cancelled'), + ('None', 'None'), + ) + customer = models.ForeignKey(CustomerProfile, on_delete=models.CASCADE) + status = models.CharField(max_length=200, choices=STATUS, default='None') + order_id = models.CharField(max_length=100, null=True, blank=True) + due_date = models.DateField(null=True) + paid = models.BooleanField(null=True, default=False) + def save(self, *args, **kwargs): + if not self.order_id: + order_count = Order.objects.filter(customer=self.customer).count() + order_count_str = str(order_count).zfill(4) + self.order_id = f"{self.customer.customer_id}-{order_count_str}" + super(Order, self).save(*args, **kwargs) + + +class OrderItem(models.Model): + order = models.ForeignKey(Order, on_delete=models.CASCADE) + item = models.ForeignKey(Item, on_delete=models.CASCADE) + recurring_cycle = models.ForeignKey(RecurringCycle, on_delete=models.SET_NULL, null=True, blank=True) + + + +class Invoice(models.Model): + invoice_number = models.CharField(max_length=100) + order = models.ForeignKey(Order, on_delete=models.SET_NULL, null=True) + date_created = models.DateField() def __str__(self): - return f"{self.service}'s charge due {self.due_date}" + return self.invoice_number class PaymentType(models.Model): @@ -40,27 +69,18 @@ class PaymentType(models.Model): return self.name -class ServicePayment(models.Model): - charge = models.ManyToManyField(Charge, null=True) +class OrderPayment(models.Model): + order = models.ManyToManyField(Order, null=True) amount = models.DecimalField(decimal_places=3, max_digits = 5) date_paid = models.DateField() type = models.ForeignKey(PaymentType, null=True, on_delete=models.SET_NULL) comment = models.TextField(null=True, blank=True) def __str__(self): - return f"Payment for {self.charge}" - - -class Invoice(models.Model): - invoice_number = models.CharField(max_length=100) - charge = models.ManyToManyField(Charge) - date_created = models.DateField() - def __str__(self): - return self.invoice_number - + return f"Payment for {self.order}" class Receipt(models.Model): receipt_number = models.CharField(max_length=100) - payment = models.ManyToManyField(ServicePayment) + payment = models.ManyToManyField(OrderPayment) date_generated = models.DateField() def __str__(self): return self.receipt_number \ No newline at end of file diff --git a/osinaweb/customercore/__pycache__/views.cpython-310.pyc b/osinaweb/customercore/__pycache__/views.cpython-310.pyc index 608b59e81a092cff9180113869c1e8ab07dd423b..1368fd28bbe6cb6cbbb5414042b0f75c6e3250a4 100644 GIT binary patch delta 1312 zcmY*X-D@0G6u*%Rs|&ywj~(QvfVSSJ8m-J&RuK5 zoo-?CP#+?ec`3BY2KommzV%=5)fWd*5PbE)Cn;Dwcal_`VSe}AbIu&jIltd`^#@a7 z5(F;6=i7(lH~*;I59{>7{S)&an_c@V0Rnp0S#!$MU2`K3EUHrKeM+l2rIUsDs8Csv6Lem7?Dow4j7hmVh=_?W4xt2 znJau4gYg|A-C+PP?9t1(jseX)=?z1e*rS;*DsV*l9g5?=xXV^alVH80s*b9M(MhOd z^rge7PDFLoxb<6TBq^C#Ad!KZPpdCi8!TA}ySh!>T^v`wDe>~y2TSmA(v zE4dLzZIKpcn@24X6?)qGJSyx&MXz@tzh|GBJdV{0?N%hXp)BXh7wu?MbdrrmnEzxi z)cA~Y8|6RzEZX^`bDF-If8e|_`37Fo_YicZQ}x=cN^NRVrsZEc=cX@XsI4I!J{}rM ztEDo2$$xVu`T~^@Gs!^TMMZ!iG-O>HFfbpXBDT*89izH5!9rRsX~hQENadomhUOm0 zY-tY}IC~Ua^e%uWjq8>)hPKj?aHKt;ioR9cHg4R<|J?~v$|H~t^74KNm3@YhLvL#x zIdqZ3F{lb(x&!*Z{DK0-A73a#J>KOP(X#kE%6TdO(K|+$@;|-VbAXOi zwWZT44UtTw^cgck>M|SeCQ|HD^B>n3!JAL()V|A}@#XxIfA5$Yxg4aS7v6_FQ3AZW zwHfWt@H$5Gzx?^JyJ)03LddF&H_>Q}=8M6~teUNGA!;YQB@&*lg*Q6gZfE1xsr3Y+ mZiiJwbDmNcUslk}e-CE8Su}m(V@Gyg3+vh`zy2?$n=>Z> delta 1161 zcmY*YTZifFCTX@)t zM^WT68vnl1{P_ELJ8r=@+b5RS&7s?Ik)b*CI$r9dgYGRhflffNhkg}@6bBeqafA`Z zw^%2pTn&>dSHl^sQ!c?-Y|uLMmXw>Ye; z$IeB!w6naMZ)9qPI*v*s%SCB!Z1l0TG@en9+!qfWqlZf4lQb8Wj=Z(cdg=ANKf2Zq z)sna3m+nYlDn9C?`rJDOi|T>5*gT_+c%8uHCg`idxuyQ_&TUQ2f!o9m>kN6I!Z%w6eCV2}Ji?w+*XO|O!c)=JvwT`^Bh zk1wm5v#N%{QT1)GVjiKC`ZGAbb%FwMQbSv_f_?Rt54Szh(uGqR&T7!AmF6HDU8}Sf zr*-n2h9!cwFSJhLyoO~BFB6o;h8T+1^zEwzooLq$O!IDkn5iGa1Xfj-&#GU;*`=$* z$c_@2kN`Jau%B=%2A{j&6V@Sy5L)1V!JmjVH6MM|)(5FtZ<}TCo)`_%UjF{(^|W+G zS9P3~c2A_;JXOC$%Z;(#U;ni+Pq=uSf_7as=CDhmP=|q1I dVoigli@56bw>b8mr`wQ)5URi9ma!Dh{Rc9o1S0?d diff --git a/osinaweb/customercore/views.py b/osinaweb/customercore/views.py index fceb5897..8185d868 100644 --- a/osinaweb/customercore/views.py +++ b/osinaweb/customercore/views.py @@ -3,6 +3,7 @@ from django.contrib.auth.decorators import login_required from django.http import JsonResponse import requests from .decorators import * +from billing.models import * import uuid import base64 @@ -56,9 +57,10 @@ def initiate_checkout(request): merchant_id = 'TEST06127800' merchant_name = 'Ositcom Sal' - order_id = str(uuid.uuid4())[:8] - - + customer = request.user.customerprofile + order = Order.objects.create(status='None', customer=customer) + order.save() + order_id = order.order_id payload = { 'apiOperation': 'INITIATE_CHECKOUT', @@ -98,6 +100,7 @@ def initiate_checkout(request): def check_order_status(request, merchant_id, order_id): api_password = '37846250a67c70e7fe9f82cf6ca81f93' url = f"https://creditlibanais-netcommerce.gateway.mastercard.com/api/rest/version/78/merchant/{merchant_id}/order/{order_id}" + order = Order.objects.get(order_id=order_id) headers = { 'Content-Type': 'application/json', @@ -109,7 +112,10 @@ def check_order_status(request, merchant_id, order_id): if response.status_code == 200: order_details = response.json() - return JsonResponse(order_details) + if order_details.get('result') == 'SUCCESS': + order.status = 'Completed' + order.save() + return JsonResponse(order_details) else: error_message = 'Failed to retrieve order details: ' + response.text return JsonResponse({'error': error_message}, status=500) diff --git a/osinaweb/db.sqlite3 b/osinaweb/db.sqlite3 index efc16a28d9d3e5302bb9903419c44ce8aff04cb1..088ee3456343ba98b14f3cc7fdd867ce8cd4e76d 100644 GIT binary patch delta 8427 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 PSh{UL0`qGT-E03R997^8 delta 11399 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 Qy|~|R24gAj(tgnVKe?oB0{{R3