diff --git a/Pipfile.lock b/Pipfile.lock index 487cd0d8..d2f4747d 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -18,19 +18,19 @@ "default": { "asgiref": { "hashes": [ - "sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac", - "sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506" + "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e", + "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed" ], "markers": "python_version >= '3.7'", - "version": "==3.6.0" + "version": "==3.7.2" }, "django": { "hashes": [ - "sha256:ad33ed68db9398f5dfb33282704925bce044bef4261cd4fb59e4e7f9ae505a78", - "sha256:c36e2ab12824e2ac36afa8b2515a70c53c7742f0d6eaefa7311ec379558db997" + "sha256:066b6debb5ac335458d2a713ed995570536c8b59a580005acb0732378d5eb1ee", + "sha256:7efa6b1f781a6119a10ac94b4794ded90db8accbe7802281cd26f8664ffed59c" ], "index": "pypi", - "version": "==4.2" + "version": "==4.2.1" }, "django-treebeard": { "hashes": [ @@ -42,11 +42,11 @@ }, "faker": { "hashes": [ - "sha256:49060d40e6659e116f53353c5771ad2f2cbcd12b15771f49e3000a3a451f13ec", - "sha256:ac903ba8cb5adbce2cdd15e5536118d484bbe01126f3c774dd9f6df77b61232d" + "sha256:80a5ea1464556c06b98bf47ea3adc7f33811a1182518d847860b1874080bd3c9", + "sha256:defe9ed618a67ebf0f3eb1895e198c2355a7128a09087a6dce342ef2253263ea" ], "index": "pypi", - "version": "==18.6.0" + "version": "==18.9.0" }, "markdown": { "hashes": [ @@ -161,22 +161,6 @@ } }, "develop": { - "aiofiles": { - "hashes": [ - "sha256:1142fa8e80dbae46bb6339573ad4c8c0841358f79c6eb50a493dceca14621bad", - "sha256:9107f1ca0b2a5553987a94a3c9959fe5b491fdf731389aa5b7b1bd0733e32de6" - ], - "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==22.1.0" - }, - "aiosqlite": { - "hashes": [ - "sha256:95ee77b91c8d2808bd08a59fbebf66270e9090c3d92ffbf260dc0db0b979577d", - "sha256:edba222e03453e094a3ce605db1b970c4b3376264e56f32e2a4959f948d66a96" - ], - "markers": "python_version >= '3.7'", - "version": "==0.19.0" - }, "alabaster": { "hashes": [ "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3", @@ -187,11 +171,11 @@ }, "anyio": { "hashes": [ - "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421", - "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3" + "sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce", + "sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0" ], - "markers": "python_full_version >= '3.6.2'", - "version": "==3.6.2" + "markers": "python_version >= '3.7'", + "version": "==3.7.0" }, "appdirs": { "hashes": [ @@ -200,6 +184,14 @@ ], "version": "==1.4.3" }, + "appnope": { + "hashes": [ + "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24", + "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e" + ], + "markers": "platform_system == 'Darwin'", + "version": "==0.1.3" + }, "argon2-cffi": { "hashes": [ "sha256:8c976986f2c5c0e5000919e6de187906cfd81fb1c72bf9d88c01177e77da7f80", @@ -245,11 +237,11 @@ }, "astroid": { "hashes": [ - "sha256:a1b8543ef9d36ea777194bc9b17f5f8678d2c56ee6a45b2c2f17eec96f242347", - "sha256:c81e1c7fbac615037744d067a9bb5f9aeb655edf59b63ee8b59585475d6f80d8" + "sha256:078e5212f9885fa85fbb0cf0101978a336190aadea6e13305409d099f71b2324", + "sha256:1039262575027b441137ab4a62a793a9b43defb42c32d5670f38686207cd780f" ], "markers": "python_full_version >= '3.7.2'", - "version": "==2.15.4" + "version": "==2.15.5" }, "asttokens": { "hashes": [ @@ -258,6 +250,14 @@ ], "version": "==2.2.1" }, + "async-lru": { + "hashes": [ + "sha256:3b87ec4f2460c52cc7916a0138cc606b584c75d1ef7d661853c95d1d3acb869a", + "sha256:d7c2b873e9af5c5a1f0a87a6c145e7e0b4eb92342b7235dda9dd5b10e950d6e2" + ], + "markers": "python_version >= '3.8'", + "version": "==2.0.2" + }, "attrs": { "hashes": [ "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", @@ -315,11 +315,11 @@ }, "certifi": { "hashes": [ - "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3", - "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18" + "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", + "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" ], "markers": "python_version >= '3.6'", - "version": "==2022.12.7" + "version": "==2023.5.7" }, "cffi": { "hashes": [ @@ -487,31 +487,6 @@ "markers": "python_version >= '3.6'", "version": "==0.1.3" }, - "cryptography": { - "hashes": [ - "sha256:05dc219433b14046c476f6f09d7636b92a1c3e5808b9a6536adf4932b3b2c440", - "sha256:0dcca15d3a19a66e63662dc8d30f8036b07be851a8680eda92d079868f106288", - "sha256:142bae539ef28a1c76794cca7f49729e7c54423f615cfd9b0b1fa90ebe53244b", - "sha256:3daf9b114213f8ba460b829a02896789751626a2a4e7a43a28ee77c04b5e4958", - "sha256:48f388d0d153350f378c7f7b41497a54ff1513c816bcbbcafe5b829e59b9ce5b", - "sha256:4df2af28d7bedc84fe45bd49bc35d710aede676e2a4cb7fc6d103a2adc8afe4d", - "sha256:4f01c9863da784558165f5d4d916093737a75203a5c5286fde60e503e4276c7a", - "sha256:7a38250f433cd41df7fcb763caa3ee9362777fdb4dc642b9a349721d2bf47404", - "sha256:8f79b5ff5ad9d3218afb1e7e20ea74da5f76943ee5edb7f76e56ec5161ec782b", - "sha256:956ba8701b4ffe91ba59665ed170a2ebbdc6fc0e40de5f6059195d9f2b33ca0e", - "sha256:a04386fb7bc85fab9cd51b6308633a3c271e3d0d3eae917eebab2fac6219b6d2", - "sha256:a95f4802d49faa6a674242e25bfeea6fc2acd915b5e5e29ac90a32b1139cae1c", - "sha256:adc0d980fd2760c9e5de537c28935cc32b9353baaf28e0814df417619c6c8c3b", - "sha256:aecbb1592b0188e030cb01f82d12556cf72e218280f621deed7d806afd2113f9", - "sha256:b12794f01d4cacfbd3177b9042198f3af1c856eedd0a98f10f141385c809a14b", - "sha256:c0764e72b36a3dc065c155e5b22f93df465da9c39af65516fe04ed3c68c92636", - "sha256:c33c0d32b8594fa647d2e01dbccc303478e16fdd7cf98652d5b3ed11aa5e5c99", - "sha256:cbaba590180cba88cb99a5f76f90808a624f18b169b90a4abb40c1fd8c19420e", - "sha256:d5a1bd0e9e2031465761dfa920c16b0065ad77321d8a8c1f5ee331021fda65e9" - ], - "markers": "python_version >= '3.6'", - "version": "==40.0.2" - }, "debugpy": { "hashes": [ "sha256:0679b7e1e3523bd7d7869447ec67b59728675aadfc038550a63a362b63029d2c", @@ -577,10 +552,10 @@ }, "fastjsonschema": { "hashes": [ - "sha256:04fbecc94300436f628517b05741b7ea009506ce8f946d40996567c669318490", - "sha256:4a30d6315a68c253cfa8f963b9697246315aa3db89f98b97235e345dedfb0b8e" + "sha256:4b90b252628ca695280924d863fe37234eebadc29c5360d322571233dc9746e0", + "sha256:f4eeb8a77cef54861dbf7424ac8ce71306f12cbb086c45131bcba2c6a4f726e3" ], - "version": "==2.16.3" + "version": "==2.17.1" }, "fqdn": { "hashes": [ @@ -591,11 +566,11 @@ }, "furo": { "hashes": [ - "sha256:4ab2be254a2d5e52792d0ca793a12c35582dd09897228a6dd47885dabd5c9521", - "sha256:b99e7867a5cc833b2b34d7230631dd6558c7a29f93071fdbb5709634bb33c5a5" + "sha256:40e09fa17c6f4b22419d122e933089226dcdb59747b5b6c79363089827dea16f", + "sha256:594a8436ddfe0c071f3a9e9a209c314a219d8341f3f1af33fdf7c69544fab9e6" ], "index": "pypi", - "version": "==2023.3.27" + "version": "==2023.5.20" }, "idna": { "hashes": [ @@ -623,26 +598,19 @@ }, "ipykernel": { "hashes": [ - "sha256:1ae6047c1277508933078163721bbb479c3e7292778a04b4bacf0874550977d6", - "sha256:302558b81f1bc22dc259fb2a0c5c7cf2f4c0bdb21b50484348f7bafe7fb71421" + "sha256:1aba0ae8453e15e9bc6b24e497ef6840114afcdb832ae597f32137fa19d42a6f", + "sha256:77aeffab056c21d16f1edccdc9e5ccbf7d96eb401bd6703610a21be8b068aadc" ], "markers": "python_version >= '3.8'", - "version": "==6.22.0" + "version": "==6.23.1" }, "ipython": { "hashes": [ - "sha256:1c80d08f04144a1994cda25569eab07fbdc4989bd8d8793e3a4ff643065ccb51", - "sha256:9c8487ac18f330c8a683fc50ab6d7bc0fcf9ef1d7a9f6ce7926938261067b81f" + "sha256:7dff3fad32b97f6488e02f87b970f309d082f758d7b7fc252e3b19ee0e432dbb", + "sha256:ffca270240fbd21b06b2974e14a86494d6d29290184e788275f55e0b55914926" ], "markers": "python_version >= '3.9'", - "version": "==8.13.1" - }, - "ipython-genutils": { - "hashes": [ - "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", - "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8" - ], - "version": "==0.2.0" + "version": "==8.13.2" }, "isoduration": { "hashes": [ @@ -675,14 +643,6 @@ "markers": "python_version >= '3.6'", "version": "==0.18.2" }, - "jeepney": { - "hashes": [ - "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806", - "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755" - ], - "markers": "sys_platform == 'linux'", - "version": "==0.8.0" - }, "jinja2": { "hashes": [ "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", @@ -693,10 +653,10 @@ }, "json5": { "hashes": [ - "sha256:1aa54b80b5e507dfe31d12b7743a642e2ffa6f70bf73b8e3d7d1d5fba83d99bd", - "sha256:4f1e196acc55b83985a51318489f345963c7ba84aa37607e49073066c562e99b" + "sha256:740c7f1b9e584a468dbb2939d8d458db3427f2c93ae2139d05f47e453eae964f", + "sha256:9ed66c3a6ca3510a976a9ef9b8c0787de24802724ab1860bc0153c7fdd589b02" ], - "version": "==0.9.11" + "version": "==0.9.14" }, "jsonpointer": { "hashes": [ @@ -737,21 +697,21 @@ "markers": "python_version >= '3.7'", "version": "==0.6.3" }, - "jupyter-server": { + "jupyter-lsp": { "hashes": [ - "sha256:9fde612791f716fd34d610cd939704a9639643744751ba66e7ee8fdc9cead07e", - "sha256:e6bc1e9e96d7c55b9ce9699ff6cb9a910581fe7349e27c40389acb67632e24c0" + "sha256:8ebbcb533adb41e5d635eb8fe82956b0aafbf0fd443b6c4bfa906edeeb8635a1", + "sha256:9e06b8b4f7dd50300b70dd1a78c0c3b0c3d8fa68e0f2d8a5d1fbab62072aca3f" ], "markers": "python_version >= '3.8'", - "version": "==2.5.0" + "version": "==2.2.0" }, - "jupyter-server-fileid": { + "jupyter-server": { "hashes": [ - "sha256:171538b7c7d08d11dbc57d4e6da196e0c258e4c2cd29249ef1e032bb423677f8", - "sha256:5b489c6fe6783c41174a728c7b81099608518387e53c3d53451a67f46a0cb7b0" + "sha256:19525a1515b5999618a91b3e99ec9f6869aa8c5ba73e0b6279fcda918b54ba36", + "sha256:ae4af349f030ed08dd78cb7ac1a03a92d886000380c9ea6283f3c542a81f4b06" ], - "markers": "python_version >= '3.7'", - "version": "==0.9.0" + "markers": "python_version >= '3.8'", + "version": "==2.6.0" }, "jupyter-server-terminals": { "hashes": [ @@ -761,29 +721,13 @@ "markers": "python_version >= '3.8'", "version": "==0.4.4" }, - "jupyter-server-ydoc": { - "hashes": [ - "sha256:969a3a1a77ed4e99487d60a74048dc9fa7d3b0dcd32e60885d835bbf7ba7be11", - "sha256:a6fe125091792d16c962cc3720c950c2b87fcc8c3ecf0c54c84e9a20b814526c" - ], - "markers": "python_version >= '3.7'", - "version": "==0.8.0" - }, - "jupyter-ydoc": { - "hashes": [ - "sha256:a3f670a69135e90493ffb91d6788efe2632bf42c6cc42a25f25c2e6eddd55a0e", - "sha256:d1a51c73ead6f6417bec0450f53c577a66abe8d43e9c2d8a1acaf7a17259f843" - ], - "markers": "python_version >= '3.7'", - "version": "==0.2.4" - }, "jupyterlab": { "hashes": [ - "sha256:373e9cfb8a72edd294be14f16662563a220cecf0fb26de7aab1af9a29b689b82", - "sha256:6aba0caa771697d02fbf409f9767b2fdb4ee32ce935940e3b9a0d5d48d994d0f" + "sha256:4dc3901f7bbfd4704c994b7a893a49955256abf57dba9831f4825e3f3165b8bb", + "sha256:f3ebd90e41d3ba1b8152c8eda2bd1a18e0de490192b4be1a6ec132517cfe43ef" ], "index": "pypi", - "version": "==3.6.3" + "version": "==4.0.1" }, "jupyterlab-pygments": { "hashes": [ @@ -970,37 +914,29 @@ "index": "pypi", "version": "==1.0.0" }, - "nbclassic": { - "hashes": [ - "sha256:aab53fa1bea084fb6ade5c538b011a4f070c69f88d72878a8e8fb356f152509f", - "sha256:e3c8b7de80046c4a36a74662a5e325386d345289906c618366d8154e03dc2322" - ], - "markers": "python_version >= '3.7'", - "version": "==0.5.6" - }, "nbclient": { "hashes": [ - "sha256:c817c0768c5ff0d60e468e017613e6eae27b6fa31e43f905addd2d24df60c125", - "sha256:d447f0e5a4cfe79d462459aec1b3dc5c2e9152597262be8ee27f7d4c02566a0d" + "sha256:25e861299e5303a0477568557c4045eccc7a34c17fc08e7959558707b9ebe548", + "sha256:f9b179cd4b2d7bca965f900a2ebf0db4a12ebff2f36a711cb66861e4ae158e55" ], - "markers": "python_full_version >= '3.7.0'", - "version": "==0.7.4" + "markers": "python_full_version >= '3.8.0'", + "version": "==0.8.0" }, "nbconvert": { "hashes": [ - "sha256:78685362b11d2e8058e70196fe83b09abed8df22d3e599cf271f4d39fdc48b9e", - "sha256:d2e95904666f1ff77d36105b9de4e0801726f93b862d5b28f69e93d99ad3b19c" + "sha256:51b6c77b507b177b73f6729dba15676e42c4e92bcb00edc8cc982ee72e7d89d7", + "sha256:af5064a9db524f9f12f4e8be7f0799524bd5b14c1adea37e34e83c95127cc818" ], "markers": "python_version >= '3.7'", - "version": "==7.3.1" + "version": "==7.4.0" }, "nbformat": { "hashes": [ - "sha256:46dac64c781f1c34dfd8acba16547024110348f9fc7eab0f31981c2a3dc48d1f", - "sha256:d910082bd3e0bffcf07eabf3683ed7dda0727a326c446eeb2922abe102e65162" + "sha256:8c8fa16d6d05062c26177754bfbfac22de644888e2ef69d27ad2a334cf2576e5", + "sha256:e98ebb6120c3efbafdee2a40af2a140cadee90bb06dd69a2a63d9551fcc7f976" ], - "markers": "python_version >= '3.7'", - "version": "==5.8.0" + "markers": "python_version >= '3.8'", + "version": "==5.9.0" }, "nest-asyncio": { "hashes": [ @@ -1010,14 +946,6 @@ "markers": "python_version >= '3.5'", "version": "==1.5.6" }, - "notebook": { - "hashes": [ - "sha256:517209568bd47261e2def27a140e97d49070602eea0d226a696f42a7f16c9a4e", - "sha256:dd17e78aefe64c768737b32bf171c1c766666a21cc79a44d37a1700771cab56f" - ], - "markers": "python_version >= '3.7'", - "version": "==6.5.4" - }, "notebook-shim": { "hashes": [ "sha256:a83496a43341c1674b093bfcebf0fe8e74cbe7eda5fd2bbc56f8e39e1486c0c7", @@ -1060,6 +988,14 @@ "markers": "python_version >= '3.10'", "version": "==1.24.3" }, + "overrides": { + "hashes": [ + "sha256:6187d8710a935d09b0bcef8238301d6ee2569d2ac1ae0ec39a8c7924e27f58ca", + "sha256:8b97c6c1e1681b78cbc9424b138d880f0803c2254c5ebaabdde57bb6c62093f2" + ], + "markers": "python_version >= '3.6'", + "version": "==7.3.1" + }, "packaging": { "hashes": [ "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", @@ -1070,34 +1006,34 @@ }, "pandas": { "hashes": [ - "sha256:00959a04a1d7bbc63d75a768540fb20ecc9e65fd80744c930e23768345a362a7", - "sha256:03e677c6bc9cfb7f93a8b617d44f6091613a5671ef2944818469be7b42114a00", - "sha256:0a514ae436b23a92366fbad8365807fc0eed15ca219690b3445dcfa33597a5cc", - "sha256:12bd6618e3cc737c5200ecabbbb5eaba8ab645a4b0db508ceeb4004bb10b060e", - "sha256:18d22cb9043b6c6804529810f492ab09d638ddf625c5dea8529239607295cb59", - "sha256:19b8e5270da32b41ebf12f0e7165efa7024492e9513fb46fb631c5022ae5709d", - "sha256:2b6fe5f7ce1cba0e74188c8473c9091ead9b293ef0a6794939f8cc7947057abd", - "sha256:320b180d125c3842c5da5889183b9a43da4ebba375ab2ef938f57bf267a3c684", - "sha256:3d099ecaa5b9e977b55cd43cf842ec13b14afa1cfa51b7e1179d90b38c53ce6a", - "sha256:6c0853d487b6c868bf107a4b270a823746175b1932093b537b9b76c639fc6f7e", - "sha256:6fa0067f2419f933101bdc6001bcea1d50812afbd367b30943417d67fbb99678", - "sha256:70a996a1d2432dadedbb638fe7d921c88b0cc4dd90374eab51bb33dc6c0c2a12", - "sha256:7b8395d335b08bc8b050590da264f94a439b4770ff16bb51798527f1dd840388", - "sha256:7bbf173d364130334e0159a9a034f573e8b44a05320995127cf676b85fd8ce86", - "sha256:8db5a644d184a38e6ed40feeb12d410d7fcc36648443defe4707022da127fc35", - "sha256:909a72b52175590debbf1d0c9e3e6bce2f1833c80c76d80bd1aa09188be768e5", - "sha256:90d1d365d77d287063c5e339f49b27bd99ef06d10a8843cf00b1a49326d492c1", - "sha256:910df06feaf9935d05247db6de452f6d59820e432c18a2919a92ffcd98f8f79b", - "sha256:99f7192d8b0e6daf8e0d0fd93baa40056684e4b4aaaef9ea78dff34168e1f2f0", - "sha256:a2564629b3a47b6aa303e024e3d84e850d36746f7e804347f64229f8c87416ea", - "sha256:a37ee35a3eb6ce523b2c064af6286c45ea1c7ff882d46e10d0945dbda7572753", - "sha256:af2449e9e984dfad39276b885271ba31c5e0204ffd9f21f287a245980b0e4091", - "sha256:e09a53a4fe8d6ae2149959a2d02e1ef2f4d2ceb285ac48f74b79798507e468b4", - "sha256:f25e23a03f7ad7211ffa30cb181c3e5f6d96a8e4cb22898af462a7333f8a74eb", - "sha256:fe7914d8ddb2d54b900cec264c090b88d141a1eed605c9539a187dbc2547f022" + "sha256:02755de164da6827764ceb3bbc5f64b35cb12394b1024fdf88704d0fa06e0e2f", + "sha256:0a1e0576611641acde15c2322228d138258f236d14b749ad9af498ab69089e2d", + "sha256:1eb09a242184092f424b2edd06eb2b99d06dc07eeddff9929e8667d4ed44e181", + "sha256:30a89d0fec4263ccbf96f68592fd668939481854d2ff9da709d32a047689393b", + "sha256:50e451932b3011b61d2961b4185382c92cc8c6ee4658dcd4f320687bb2d000ee", + "sha256:51a93d422fbb1bd04b67639ba4b5368dffc26923f3ea32a275d2cc450f1d1c86", + "sha256:598e9020d85a8cdbaa1815eb325a91cfff2bb2b23c1442549b8a3668e36f0f77", + "sha256:66d00300f188fa5de73f92d5725ced162488f6dc6ad4cecfe4144ca29debe3b8", + "sha256:69167693cb8f9b3fc060956a5d0a0a8dbfed5f980d9fd2c306fb5b9c855c814c", + "sha256:6d6d10c2142d11d40d6e6c0a190b1f89f525bcf85564707e31b0a39e3b398e08", + "sha256:713f2f70abcdade1ddd68fc91577cb090b3544b07ceba78a12f799355a13ee44", + "sha256:7376e13d28eb16752c398ca1d36ccfe52bf7e887067af9a0474de6331dd948d2", + "sha256:77550c8909ebc23e56a89f91b40ad01b50c42cfbfab49b3393694a50549295ea", + "sha256:7b21cb72958fc49ad757685db1919021d99650d7aaba676576c9e88d3889d456", + "sha256:9ebb9f1c22ddb828e7fd017ea265a59d80461d5a79154b49a4207bd17514d122", + "sha256:a18e5c72b989ff0f7197707ceddc99828320d0ca22ab50dd1b9e37db45b010c0", + "sha256:a6b5f14cd24a2ed06e14255ff40fe2ea0cfaef79a8dd68069b7ace74bd6acbba", + "sha256:b42b120458636a981077cfcfa8568c031b3e8709701315e2bfa866324a83efa8", + "sha256:c4af689352c4fe3d75b2834933ee9d0ccdbf5d7a8a7264f0ce9524e877820c08", + "sha256:c7319b6e68de14e6209460f72a8d1ef13c09fb3d3ef6c37c1e65b35d50b5c145", + "sha256:cf3f0c361a4270185baa89ec7ab92ecaa355fe783791457077473f974f654df5", + "sha256:dd46bde7309088481b1cf9c58e3f0e204b9ff9e3244f441accd220dd3365ce7c", + "sha256:dd5476b6c3fe410ee95926873f377b856dbc4e81a9c605a0dc05aaccc6a7c6c6", + "sha256:e69140bc2d29a8556f55445c15f5794490852af3de0f609a24003ef174528b79", + "sha256:f908a77cbeef9bbd646bd4b81214cbef9ac3dda4181d5092a4aa9797d1bc7774" ], "index": "pypi", - "version": "==2.0.1" + "version": "==2.0.2" }, "pandocfilters": { "hashes": [ @@ -1167,19 +1103,19 @@ }, "platformdirs": { "hashes": [ - "sha256:47692bc24c1958e8b0f13dd727307cff1db103fca36399f457da8e05f222fdc4", - "sha256:7954a68d0ba23558d753f73437c55f89027cf8f5108c19844d4b82e5af396335" + "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f", + "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5" ], "markers": "python_version >= '3.7'", - "version": "==3.5.0" + "version": "==3.5.1" }, "prometheus-client": { "hashes": [ - "sha256:0836af6eb2c8f4fed712b2f279f6c0a8bbab29f9f4aa15276b91c7cb0d1616ab", - "sha256:a03e35b359f14dd1630898543e2120addfdeacd1a6069c1367ae90fd93ad3f48" + "sha256:9c3b26f1535945e85b8934fb374678d263137b78ef85f305b1156c7c881cd11b", + "sha256:a77b708cf083f4d1a3fb3ce5c95b4afa32b9c521ae363354a4a910204ea095ce" ], "markers": "python_version >= '3.6'", - "version": "==0.16.0" + "version": "==0.17.0" }, "prompt-toolkit": { "hashes": [ @@ -1214,6 +1150,7 @@ "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220" ], + "markers": "os_name != 'nt'", "version": "==0.7.0" }, "pure-eval": { @@ -1240,11 +1177,11 @@ }, "pylint": { "hashes": [ - "sha256:761907349e699f8afdcd56c4fe02f3021ab5b3a0fc26d19a9bfdc66c7d0d5cd5", - "sha256:a6cbb4c6e96eab4a3c7de7c6383c512478f58f88d95764507d84c899d656a89a" + "sha256:5dcf1d9e19f41f38e4e85d10f511e5b9c35e1aa74251bf95cdd8cb23584e2db1", + "sha256:7a1145fb08c251bdb5cca11739722ce64a63db479283d10ce718b2460e54123c" ], "index": "pypi", - "version": "==2.17.3" + "version": "==2.17.4" }, "pyrsistent": { "hashes": [ @@ -1358,86 +1295,86 @@ }, "pyzmq": { "hashes": [ - "sha256:032f5c8483c85bf9c9ca0593a11c7c749d734ce68d435e38c3f72e759b98b3c9", - "sha256:08bfcc21b5997a9be4fefa405341320d8e7f19b4d684fb9c0580255c5bd6d695", - "sha256:1a843d26a8da1b752c74bc019c7b20e6791ee813cd6877449e6a1415589d22ff", - "sha256:1f124cb73f1aa6654d31b183810febc8505fd0c597afa127c4f40076be4574e0", - "sha256:1f82906a2d8e4ee310f30487b165e7cc8ed09c009e4502da67178b03083c4ce0", - "sha256:21ec0bf4831988af43c8d66ba3ccd81af2c5e793e1bf6790eb2d50e27b3c570a", - "sha256:24683285cc6b7bf18ad37d75b9db0e0fefe58404e7001f1d82bf9e721806daa7", - "sha256:24abbfdbb75ac5039205e72d6c75f10fc39d925f2df8ff21ebc74179488ebfca", - "sha256:25e6873a70ad5aa31e4a7c41e5e8c709296edef4a92313e1cd5fc87bbd1874e2", - "sha256:269968f2a76c0513490aeb3ba0dc3c77b7c7a11daa894f9d1da88d4a0db09835", - "sha256:26b0358e8933990502f4513c991c9935b6c06af01787a36d133b7c39b1df37fa", - "sha256:28fdb9224a258134784a9cf009b59265a9dde79582fb750d4e88a6bcbc6fa3dc", - "sha256:2b9c9cc965cdf28381e36da525dcb89fc1571d9c54800fdcd73e3f73a2fc29bd", - "sha256:2da6813b7995b6b1d1307329c73d3e3be2fd2d78e19acfc4eff2e27262732388", - "sha256:3059a6a534c910e1d5d068df42f60d434f79e6cc6285aa469b384fa921f78cf8", - "sha256:312b3f0f066b4f1d17383aae509bacf833ccaf591184a1f3c7a1661c085063ae", - "sha256:34a6fddd159ff38aa9497b2e342a559f142ab365576284bc8f77cb3ead1f79c5", - "sha256:374b55516393bfd4d7a7daa6c3b36d6dd6a31ff9d2adad0838cd6a203125e714", - "sha256:38d9f78d69bcdeec0c11e0feb3bc70f36f9b8c44fc06e5d06d91dc0a21b453c7", - "sha256:4a31992a8f8d51663ebf79df0df6a04ffb905063083d682d4380ab8d2c67257c", - "sha256:4a4b4261eb8f9ed71f63b9eb0198dd7c934aa3b3972dac586d0ef502ba9ab08b", - "sha256:510d8e55b3a7cd13f8d3e9121edf0a8730b87d925d25298bace29a7e7bc82810", - "sha256:531e36d9fcd66f18de27434a25b51d137eb546931033f392e85674c7a7cea853", - "sha256:54a96cf77684a3a537b76acfa7237b1e79a8f8d14e7f00e0171a94b346c5293e", - "sha256:56a94ab1d12af982b55ca96c6853db6ac85505e820d9458ac76364c1998972f4", - "sha256:5c5fbb229e40a89a2fe73d0c1181916f31e30f253cb2d6d91bea7927c2e18413", - "sha256:5d496815074e3e3d183fe2c7fcea2109ad67b74084c254481f87b64e04e9a471", - "sha256:5eaeae038c68748082137d6896d5c4db7927e9349237ded08ee1bbd94f7361c9", - "sha256:62ec8d979f56c0053a92b2b6a10ff54b9ec8a4f187db2b6ec31ee3dd6d3ca6e2", - "sha256:64812f29d6eee565e129ca14b0c785744bfff679a4727137484101b34602d1a7", - "sha256:6526d097b75192f228c09d48420854d53dfbc7abbb41b0e26f363ccb26fbc177", - "sha256:659e62e1cbb063151c52f5b01a38e1df6b54feccfa3e2509d44c35ca6d7962ee", - "sha256:65c19a63b4a83ae45d62178b70223adeee5f12f3032726b897431b6553aa25af", - "sha256:67da1c213fbd208906ab3470cfff1ee0048838365135a9bddc7b40b11e6d6c89", - "sha256:6a821a506822fac55d2df2085a52530f68ab15ceed12d63539adc32bd4410f6e", - "sha256:6a979e59d2184a0c8f2ede4b0810cbdd86b64d99d9cc8a023929e40dce7c86cc", - "sha256:6b8c1bbb70e868dc88801aa532cae6bd4e3b5233784692b786f17ad2962e5149", - "sha256:6fadc60970714d86eff27821f8fb01f8328dd36bebd496b0564a500fe4a9e354", - "sha256:715cff7644a80a7795953c11b067a75f16eb9fc695a5a53316891ebee7f3c9d5", - "sha256:77942243ff4d14d90c11b2afd8ee6c039b45a0be4e53fb6fa7f5e4fd0b59da39", - "sha256:7b504ae43d37e282301da586529e2ded8b36d4ee2cd5e6db4386724ddeaa6bbc", - "sha256:827bf60e749e78acb408a6c5af6688efbc9993e44ecc792b036ec2f4b4acf485", - "sha256:8280ada89010735a12b968ec3ea9a468ac2e04fddcc1cede59cb7f5178783b9c", - "sha256:83d822e8687621bed87404afc1c03d83fa2ce39733d54c2fd52d8829edb8a7ff", - "sha256:8560756318ec7c4c49d2c341012167e704b5a46d9034905853c3d1ade4f55bee", - "sha256:85762712b74c7bd18e340c3639d1bf2f23735a998d63f46bb6584d904b5e401d", - "sha256:88649b19ede1cab03b96b66c364cbbf17c953615cdbc844f7f6e5f14c5e5261c", - "sha256:9a2e5fe42dfe6b73ca120b97ac9f34bfa8414feb15e00e37415dbd51cf227ef6", - "sha256:9af0bb0277e92f41af35e991c242c9c71920169d6aa53ade7e444f338f4c8128", - "sha256:9bdc40efb679b9dcc39c06d25629e55581e4c4f7870a5e88db4f1c51ce25e20d", - "sha256:9e1d2f2d86fc75ed7f8845a992c5f6f1ab5db99747fb0d78b5e4046d041164d2", - "sha256:a2e92ff20ad5d13266bc999a29ed29a3b5b101c21fdf4b2cf420c09db9fb690e", - "sha256:a35960c8b2f63e4ef67fd6731851030df68e4b617a6715dd11b4b10312d19fef", - "sha256:a6f6ae12478fdc26a6d5fdb21f806b08fa5403cd02fd312e4cb5f72df078f96f", - "sha256:a9b5eeb5278a8a636bb0abdd9ff5076bcbb836cd2302565df53ff1fa7d106d54", - "sha256:ab046e9cb902d1f62c9cc0eca055b1d11108bdc271caf7c2171487298f229b56", - "sha256:ab2c056ac503f25a63f6c8c6771373e2a711b98b304614151dfb552d3d6c81f6", - "sha256:abbce982a17c88d2312ec2cf7673985d444f1beaac6e8189424e0a0e0448dbb3", - "sha256:ac178e666c097c8d3deb5097b58cd1316092fc43e8ef5b5fdb259b51da7e7315", - "sha256:ad761cfbe477236802a7ab2c080d268c95e784fe30cafa7e055aacd1ca877eb0", - "sha256:affec1470351178e892121b3414c8ef7803269f207bf9bef85f9a6dd11cde264", - "sha256:b164cc3c8acb3d102e311f2eb6f3c305865ecb377e56adc015cb51f721f1dda6", - "sha256:b48616a09d7df9dbae2f45a0256eee7b794b903ddc6d8657a9948669b345f220", - "sha256:b491998ef886662c1f3d49ea2198055a9a536ddf7430b051b21054f2a5831800", - "sha256:b733076ff46e7db5504c5e7284f04a9852c63214c74688bdb6135808531755a3", - "sha256:c8fedc3ccd62c6b77dfe6f43802057a803a411ee96f14e946f4a76ec4ed0e117", - "sha256:cb1f69a0a2a2b1aae8412979dd6293cc6bcddd4439bf07e4758d864ddb112354", - "sha256:cca8524b61c0eaaa3505382dc9b9a3bc8165f1d6c010fdd1452c224225a26689", - "sha256:cfb9f7eae02d3ac42fbedad30006b7407c984a0eb4189a1322241a20944d61e5", - "sha256:d4427b4a136e3b7f85516c76dd2e0756c22eec4026afb76ca1397152b0ca8145", - "sha256:d488c5c8630f7e782e800869f82744c3aca4aca62c63232e5d8c490d3d66956a", - "sha256:dd771a440effa1c36d3523bc6ba4e54ff5d2e54b4adcc1e060d8f3ca3721d228", - "sha256:ed15e3a2c3c2398e6ae5ce86d6a31b452dfd6ad4cd5d312596b30929c4b6e182", - "sha256:edbbf06cc2719889470a8d2bf5072bb00f423e12de0eb9ffec946c2c9748e149", - "sha256:eef2a0b880ab40aca5a878933376cb6c1ec483fba72f7f34e015c0f675c90b20", - "sha256:f7c8b8368e84381ae7c57f1f5283b029c888504aaf4949c32e6e6fb256ec9bf0", - "sha256:ffc71111433bd6ec8607a37b9211f4ef42e3d3b271c6d76c813669834764b248" + "sha256:01f06f33e12497dca86353c354461f75275a5ad9eaea181ac0dc1662da8074fa", + "sha256:0b6b42f7055bbc562f63f3df3b63e3dd1ebe9727ff0f124c3aa7bcea7b3a00f9", + "sha256:0c4fc2741e0513b5d5a12fe200d6785bbcc621f6f2278893a9ca7bed7f2efb7d", + "sha256:108c96ebbd573d929740d66e4c3d1bdf31d5cde003b8dc7811a3c8c5b0fc173b", + "sha256:13bbe36da3f8aaf2b7ec12696253c0bf6ffe05f4507985a8844a1081db6ec22d", + "sha256:154bddda2a351161474b36dba03bf1463377ec226a13458725183e508840df89", + "sha256:19d0383b1f18411d137d891cab567de9afa609b214de68b86e20173dc624c101", + "sha256:1a6169e69034eaa06823da6a93a7739ff38716142b3596c180363dee729d713d", + "sha256:1fc56a0221bdf67cfa94ef2d6ce5513a3d209c3dfd21fed4d4e87eca1822e3a3", + "sha256:2a21fec5c3cea45421a19ccbe6250c82f97af4175bc09de4d6dd78fb0cb4c200", + "sha256:2b15247c49d8cbea695b321ae5478d47cffd496a2ec5ef47131a9e79ddd7e46c", + "sha256:2f5efcc29056dfe95e9c9db0dfbb12b62db9c4ad302f812931b6d21dd04a9119", + "sha256:2f666ae327a6899ff560d741681fdcdf4506f990595201ed39b44278c471ad98", + "sha256:332616f95eb400492103ab9d542b69d5f0ff628b23129a4bc0a2fd48da6e4e0b", + "sha256:33d5c8391a34d56224bccf74f458d82fc6e24b3213fc68165c98b708c7a69325", + "sha256:3575699d7fd7c9b2108bc1c6128641a9a825a58577775ada26c02eb29e09c517", + "sha256:3830be8826639d801de9053cf86350ed6742c4321ba4236e4b5568528d7bfed7", + "sha256:3a522510e3434e12aff80187144c6df556bb06fe6b9d01b2ecfbd2b5bfa5c60c", + "sha256:3bed53f7218490c68f0e82a29c92335daa9606216e51c64f37b48eb78f1281f4", + "sha256:414b8beec76521358b49170db7b9967d6974bdfc3297f47f7d23edec37329b00", + "sha256:442d3efc77ca4d35bee3547a8e08e8d4bb88dadb54a8377014938ba98d2e074a", + "sha256:47b915ba666c51391836d7ed9a745926b22c434efa76c119f77bcffa64d2c50c", + "sha256:48e5e59e77c1a83162ab3c163fc01cd2eebc5b34560341a67421b09be0891287", + "sha256:4a82faae00d1eed4809c2f18b37f15ce39a10a1c58fe48b60ad02875d6e13d80", + "sha256:4a983c8694667fd76d793ada77fd36c8317e76aa66eec75be2653cef2ea72883", + "sha256:4c2fc7aad520a97d64ffc98190fce6b64152bde57a10c704b337082679e74f67", + "sha256:4cb27ef9d3bdc0c195b2dc54fcb8720e18b741624686a81942e14c8b67cc61a6", + "sha256:4d67609b37204acad3d566bb7391e0ecc25ef8bae22ff72ebe2ad7ffb7847158", + "sha256:5482f08d2c3c42b920e8771ae8932fbaa0a67dff925fc476996ddd8155a170f3", + "sha256:5489738a692bc7ee9a0a7765979c8a572520d616d12d949eaffc6e061b82b4d1", + "sha256:5693dcc4f163481cf79e98cf2d7995c60e43809e325b77a7748d8024b1b7bcba", + "sha256:58416db767787aedbfd57116714aad6c9ce57215ffa1c3758a52403f7c68cff5", + "sha256:5873d6a60b778848ce23b6c0ac26c39e48969823882f607516b91fb323ce80e5", + "sha256:5af31493663cf76dd36b00dafbc839e83bbca8a0662931e11816d75f36155897", + "sha256:5e7fbcafa3ea16d1de1f213c226005fea21ee16ed56134b75b2dede5a2129e62", + "sha256:65346f507a815a731092421d0d7d60ed551a80d9b75e8b684307d435a5597425", + "sha256:6581e886aec3135964a302a0f5eb68f964869b9efd1dbafdebceaaf2934f8a68", + "sha256:69511d604368f3dc58d4be1b0bad99b61ee92b44afe1cd9b7bd8c5e34ea8248a", + "sha256:7018289b402ebf2b2c06992813523de61d4ce17bd514c4339d8f27a6f6809492", + "sha256:71c7b5896e40720d30cd77a81e62b433b981005bbff0cb2f739e0f8d059b5d99", + "sha256:75217e83faea9edbc29516fc90c817bc40c6b21a5771ecb53e868e45594826b0", + "sha256:7e23a8c3b6c06de40bdb9e06288180d630b562db8ac199e8cc535af81f90e64b", + "sha256:80c41023465d36280e801564a69cbfce8ae85ff79b080e1913f6e90481fb8957", + "sha256:831ba20b660b39e39e5ac8603e8193f8fce1ee03a42c84ade89c36a251449d80", + "sha256:851fb2fe14036cfc1960d806628b80276af5424db09fe5c91c726890c8e6d943", + "sha256:8751f9c1442624da391bbd92bd4b072def6d7702a9390e4479f45c182392ff78", + "sha256:8b45d722046fea5a5694cba5d86f21f78f0052b40a4bbbbf60128ac55bfcc7b6", + "sha256:8b697774ea8273e3c0460cf0bba16cd85ca6c46dfe8b303211816d68c492e132", + "sha256:90146ab578931e0e2826ee39d0c948d0ea72734378f1898939d18bc9c823fcf9", + "sha256:9301cf1d7fc1ddf668d0abbe3e227fc9ab15bc036a31c247276012abb921b5ff", + "sha256:95bd3a998d8c68b76679f6b18f520904af5204f089beebb7b0301d97704634dd", + "sha256:968b0c737797c1809ec602e082cb63e9824ff2329275336bb88bd71591e94a90", + "sha256:97d984b1b2f574bc1bb58296d3c0b64b10e95e7026f8716ed6c0b86d4679843f", + "sha256:9e68ae9864d260b18f311b68d29134d8776d82e7f5d75ce898b40a88df9db30f", + "sha256:adecf6d02b1beab8d7c04bc36f22bb0e4c65a35eb0b4750b91693631d4081c70", + "sha256:af56229ea6527a849ac9fb154a059d7e32e77a8cba27e3e62a1e38d8808cb1a5", + "sha256:b324fa769577fc2c8f5efcd429cef5acbc17d63fe15ed16d6dcbac2c5eb00849", + "sha256:b5a07c4f29bf7cb0164664ef87e4aa25435dcc1f818d29842118b0ac1eb8e2b5", + "sha256:bad172aba822444b32eae54c2d5ab18cd7dee9814fd5c7ed026603b8cae2d05f", + "sha256:bdca18b94c404af6ae5533cd1bc310c4931f7ac97c148bbfd2cd4bdd62b96253", + "sha256:be24a5867b8e3b9dd5c241de359a9a5217698ff616ac2daa47713ba2ebe30ad1", + "sha256:be86a26415a8b6af02cd8d782e3a9ae3872140a057f1cadf0133de685185c02b", + "sha256:c66b7ff2527e18554030319b1376d81560ca0742c6e0b17ff1ee96624a5f1afd", + "sha256:c8398a1b1951aaa330269c35335ae69744be166e67e0ebd9869bdc09426f3871", + "sha256:cad9545f5801a125f162d09ec9b724b7ad9b6440151b89645241d0120e119dcc", + "sha256:cb6d161ae94fb35bb518b74bb06b7293299c15ba3bc099dccd6a5b7ae589aee3", + "sha256:d40682ac60b2a613d36d8d3a0cd14fbdf8e7e0618fbb40aa9fa7b796c9081584", + "sha256:d6128d431b8dfa888bf51c22a04d48bcb3d64431caf02b3cb943269f17fd2994", + "sha256:dbc466744a2db4b7ca05589f21ae1a35066afada2f803f92369f5877c100ef62", + "sha256:ddbef8b53cd16467fdbfa92a712eae46dd066aa19780681a2ce266e88fbc7165", + "sha256:e21cc00e4debe8f54c3ed7b9fcca540f46eee12762a9fa56feb8512fd9057161", + "sha256:eb52e826d16c09ef87132c6e360e1879c984f19a4f62d8a935345deac43f3c12", + "sha256:f0d9e7ba6a815a12c8575ba7887da4b72483e4cfc57179af10c9b937f3f9308f", + "sha256:f1e931d9a92f628858a50f5bdffdfcf839aebe388b82f9d2ccd5d22a38a789dc", + "sha256:f45808eda8b1d71308c5416ef3abe958f033fdbb356984fabbfc7887bed76b3f", + "sha256:f6d39e42a0aa888122d1beb8ec0d4ddfb6c6b45aecb5ba4013c27e2f28657765", + "sha256:fc34fdd458ff77a2a00e3c86f899911f6f269d393ca5675842a6e92eea565bae" ], "markers": "python_version >= '3.6'", - "version": "==25.0.2" + "version": "==25.1.0" }, "readme-renderer": { "hashes": [ @@ -1449,11 +1386,11 @@ }, "requests": { "hashes": [ - "sha256:e8f3c9be120d3333921d213eef078af392fba3933ab7ed2d1cba3b56f2568c3b", - "sha256:f2e34a75f4749019bb0e3effb66683630e4ffeaf75819fb51bebef1bf5aef059" + "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", + "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" ], "markers": "python_version >= '3.7'", - "version": "==2.29.0" + "version": "==2.31.0" }, "requests-toolbelt": { "hashes": [ @@ -1489,19 +1426,11 @@ }, "rich": { "hashes": [ - "sha256:2d11b9b8dd03868f09b4fffadc84a6a8cda574e40dc90821bd845720ebb8e89c", - "sha256:69cdf53799e63f38b95b9bf9c875f8c90e78dd62b2f00c13a911c7a3b9fa4704" + "sha256:76f6b65ea7e5c5d924ba80e322231d7cb5b5981aa60bfc1e694f1bc097fe6fe1", + "sha256:d204aadb50b936bf6b1a695385429d192bc1fdaf3e8b907e8e26f4c4e4b5bf75" ], "markers": "python_full_version >= '3.7.0'", - "version": "==13.3.5" - }, - "secretstorage": { - "hashes": [ - "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77", - "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99" - ], - "markers": "sys_platform == 'linux'", - "version": "==3.3.3" + "version": "==13.4.1" }, "send2trash": { "hashes": [ @@ -1656,20 +1585,20 @@ }, "tornado": { "hashes": [ - "sha256:1285f0691143f7ab97150831455d4db17a267b59649f7bd9700282cba3d5e771", - "sha256:3455133b9ff262fd0a75630af0a8ee13564f25fb4fd3d9ce239b8a7d3d027bf8", - "sha256:5e2f49ad371595957c50e42dd7e5c14d64a6843a3cf27352b69c706d1b5918af", - "sha256:81c17e0cc396908a5e25dc8e9c5e4936e6dfd544c9290be48bd054c79bcad51e", - "sha256:90f569a35a8ec19bde53aa596952071f445da678ec8596af763b9b9ce07605e6", - "sha256:9661aa8bc0e9d83d757cd95b6f6d1ece8ca9fd1ccdd34db2de381e25bf818233", - "sha256:a27a1cfa9997923f80bdd962b3aab048ac486ad8cfb2f237964f8ab7f7eb824b", - "sha256:b4e7b956f9b5e6f9feb643ea04f07e7c6b49301e03e0023eedb01fa8cf52f579", - "sha256:d7117f3c7ba5d05813b17a1f04efc8e108a1b811ccfddd9134cc68553c414864", - "sha256:db181eb3df8738613ff0a26f49e1b394aade05034b01200a63e9662f347d4415", - "sha256:ffdce65a281fd708da5a9def3bfb8f364766847fa7ed806821a69094c9629e8a" + "sha256:05615096845cf50a895026f749195bf0b10b8909f9be672f50b0fe69cba368e4", + "sha256:0c325e66c8123c606eea33084976c832aa4e766b7dff8aedd7587ea44a604cdf", + "sha256:29e71c847a35f6e10ca3b5c2990a52ce38b233019d8e858b755ea6ce4dcdd19d", + "sha256:4b927c4f19b71e627b13f3db2324e4ae660527143f9e1f2e2fb404f3a187e2ba", + "sha256:5b17b1cf5f8354efa3d37c6e28fdfd9c1c1e5122f2cb56dac121ac61baa47cbe", + "sha256:6a0848f1aea0d196a7c4f6772197cbe2abc4266f836b0aac76947872cd29b411", + "sha256:7efcbcc30b7c654eb6a8c9c9da787a851c18f8ccd4a5a3a95b05c7accfa068d2", + "sha256:834ae7540ad3a83199a8da8f9f2d383e3c3d5130a328889e4cc991acc81e87a0", + "sha256:b46a6ab20f5c7c1cb949c72c1994a4585d2eaa0be4853f50a03b5031e964fc7c", + "sha256:c2de14066c4a38b4ecbbcd55c5cc4b5340eb04f1c5e81da7451ef555859c833f", + "sha256:c367ab6c0393d71171123ca5515c61ff62fe09024fa6bf299cd1339dc9456829" ], "markers": "python_version >= '3.8'", - "version": "==6.3.1" + "version": "==6.3.2" }, "traitlets": { "hashes": [ @@ -1687,6 +1616,14 @@ "index": "pypi", "version": "==4.0.2" }, + "typing-extensions": { + "hashes": [ + "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c", + "sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98" + ], + "markers": "python_version >= '3.7'", + "version": "==4.6.2" + }, "tzdata": { "hashes": [ "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a", @@ -1704,11 +1641,11 @@ }, "urllib3": { "hashes": [ - "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305", - "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42" + "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc", + "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.15" + "markers": "python_version >= '3.7'", + "version": "==2.0.2" }, "wcwidth": { "hashes": [ @@ -1733,11 +1670,11 @@ }, "websocket-client": { "hashes": [ - "sha256:3f09e6d8230892547132177f575a4e3e73cfdf06526e20cc02aa1c3b47184d40", - "sha256:cdf5877568b7e83aa7cf2244ab56a3213de587bbe0ce9d8b9600fc77b455d89e" + "sha256:c7d67c13b928645f259d9b847ab5b57fd2d127213ca41ebd880de1f553b7c23b", + "sha256:f8c64e28cd700e7ba1f04350d66422b6833b82a796b525a51e740b8cc8dab4b1" ], "markers": "python_version >= '3.7'", - "version": "==1.5.1" + "version": "==1.5.2" }, "wrapt": { "hashes": [ @@ -1820,85 +1757,6 @@ "markers": "python_version >= '3.11'", "version": "==1.15.0" }, - "y-py": { - "hashes": [ - "sha256:05f805b58422d5d7c8e7e8e2141d1c3cac4daaa4557ae6a9b84b141fe8d6289e", - "sha256:065f90501cf008375d70be6ce72dd41745e09d088f0b545f5f914d2c3f04f7ae", - "sha256:0c0e333c20b0a6ce4a5851203d45898ab93f16426c342420b931e190c5b71d3d", - "sha256:13b9d2959d9a26536b6ad118fb026ff19bd79da52e4addf6f3a562e7c01d516e", - "sha256:1906f13e8d5ebfbd9c7948f57bc6f6f53b451b19c99350f42a0f648147a8acfe", - "sha256:1f54625b9ed4e787872c45d3044dcfd04c0da4258d9914f3d32308830b35246c", - "sha256:202b2a3e42e0a1eaedee26f8a3bc73cd9f994c4c2b15511ea56b9838178eb380", - "sha256:2532ea5aefb223fd688c93860199d348a7601d814aac9e8784d816314588ddeb", - "sha256:25637e3d011ca6f877a24f3083ff2549d1d619406d7e8a1455c445527205046c", - "sha256:2692c808bf28f797f8d693f45dc86563ac3b1626579f67ce9546dca69644d687", - "sha256:27c1e9a866146d250e9e16d99fe22a40c82f5b592ab85da97e5679fc3841c7ce", - "sha256:2ffebe5e62cbfee6e24593927dedba77dc13ac4cfb9c822074ab566b1fb63d59", - "sha256:50cfa0532bcee27edb8c64743b49570e28bb76a00cd384ead1d84b6f052d9368", - "sha256:55098440e32339c2dc3d652fb36bb77a4927dee5fd4ab0cb1fe12fdd163fd4f5", - "sha256:5dbd8d177ec7b9fef4a7b6d22eb2f8d5606fd5aac31cf2eab0dc18f0b3504c7c", - "sha256:63ef8e5b76cd54578a7fd5f72d8c698d9ccd7c555c7900ebfd38a24d397c3b15", - "sha256:73200c59bb253b880825466717941ac57267f2f685b053e183183cb6fe82874d", - "sha256:7353af0e9c1f42fbf0ab340e253eeb333d58c890fa91d3eadb1b9adaf9336732", - "sha256:742c486d5b792c4ad76e09426161302edddca85efe826fa01dcee50907326cd7", - "sha256:753aaae817d658a1e9d271663439d8e83d9d8effa45590ecdcadc600c7cf77e3", - "sha256:76b3480e7037ac9390c450e2aff9e46e2c9e61520c0d88afe228110ec728adc5", - "sha256:800e73d2110b97a74c52db2c8ce03a78e96f0d66a7e0c87d8254170a67c2db0e", - "sha256:85585e669d7679126e4a04e4bc0a063a641175a74eecfe47539e8da3e5b1da6e", - "sha256:8d4dfc276f988175baaa4ab321c3321a16ce33db3356c9bc5f4dea0db3de55aa", - "sha256:91be189fae8ba242528333e266e38d65cae3d9a09fe45867fab8578a3ddf2ea2", - "sha256:9484a3fc33f812234e58a5ee834b42bb0a628054d61b5c06c323aa56c12e557d", - "sha256:9513ae81fcc805671ae134c4c7421ca322acf92ce8b33817e1775ea8c0176973", - "sha256:95d13b38c9055d607565b77cbae12e2bf0c1671c5cb8f2ee2e1230d41d2d6d34", - "sha256:9983e99e3a61452b39ffce98206c7e4c6d260f4e917c8fe53fb54aaf25df89a3", - "sha256:9a59603cf42c20d02ee5add2e3d0ce48e89c480a2a02f642fb77f142c4f37958", - "sha256:a57d81260e048caacf43a2f851766687f53e8a8356df6947fb0eee7336a7e2de", - "sha256:a7977eeaceaeb0dfffcc5643c985c337ebc33a0b1d792ae0a9b1331cdd97366f", - "sha256:add793f5f5c7c7a3eb1b09ffc771bdaae10a0bd482a370bf696b83f8dee8d1b4", - "sha256:ae82a6d9cbaff8cb7505e81b5b7f9cd7756bb7e7110aef7914375fe56b012a90", - "sha256:af6df5ec1d66ee2d962026635d60e84ad35fc01b2a1e36b993360c0ce60ae349", - "sha256:afa9a11aa2880dd8689894f3269b653e6d3bd1956963d5329be9a5bf021dab62", - "sha256:b0ed760e6aa5316227a0ba2d5d29634a4ef2d72c8bc55169ac01664e17e4b536", - "sha256:b44473bb32217c78e18db66f497f6c8be33e339bab5f52398bb2468c904d5140", - "sha256:b67dad339f9b6701f74ff7a6e901c7909eca4eea02cf955b28d87a42650bd1be", - "sha256:bc9052a814e8b7ec756371a191f38de68b956437e0bb429c2dd503e658f298f9", - "sha256:c1f5f287cc7ae127ed6a2fb1546e631b316a41d087d7d2db9caa3e5f59906dcf", - "sha256:c3ae6d22b7cc599220a26b06da6ead9fd582eea5fdb6273b06fa3f060d0a26a7", - "sha256:c42f3a6cd20153925b00c49af855a3277989d411bb8ea849095be943ee160821", - "sha256:c7ca64a2a97f708569dcabd55865915943e30267bf6d26c4d212d005951efe62", - "sha256:caf9b1feb69379d424a1d3d7c899b8e0389a3fb3131d39c3c03dcc3d4a93dbdc", - "sha256:cb68445414940efe547291340e91604c7b8379b60822678ef29f4fc2a0e11c62", - "sha256:cc8e5f38842a4b043c9592bfa9a740147ddb8fac2d7a5b7bf6d52466c090ec23", - "sha256:cd6f373dbf592ad83aaf95c16abebc8678928e49bd509ebd593259e1908345ae", - "sha256:d2da2a9e28dceab4832945a745cad507579f52b4d0c9e2f54ae156eb56875861", - "sha256:d373c6bb8e21d5f7ec0833b76fa1ab480086ada602ef5bbf4724a25a21a00b6a", - "sha256:d722d6a27230c1f395535da5cee6a9a16497c6343afd262c846090075c083009", - "sha256:db1ac7f2d1862eb4c448cf76183399d555a63dbe2452bafecb1c2f691e36d687", - "sha256:df78a0409dca11554a4b6442d7a8e61f762c3cfc78d55d98352392869a6b9ae0", - "sha256:e30fe2491d095c6d695a2c96257967fd3e2497f0f777030c8492d03c18d46e2a", - "sha256:e370ce076781adea161b04d2f666e8b4f89bc7e8927ef842fbb0283d3bfa73e0", - "sha256:ecd3cb0d13ac92e7b9235d1024dba9af0788161246f12dcf1f635d634ccb206a", - "sha256:ed0fd5265905cc7e23709479bc152d69f4972dec32fa322d20cb77f749707e78", - "sha256:f6d87d0c2e87990bc00c049742d36a5dbbb1510949459af17198728890ee748a", - "sha256:f7434c77cd23592973ed63341b8d337e6aebaba5ed40d7f22e2d43dfd0c3a56e", - "sha256:f8b67ae37af8aac6160fda66c0f73bcdf65c06da9022eb76192c3fc45cfab994", - "sha256:f8f238144a302f17eb26b122cad9382fcff5ec6653b8a562130b9a5e44010098", - "sha256:fa685f7e43ce490dfb1e392ac48f584b75cd21f05dc526c160d15308236ce8a0", - "sha256:fce5feb57f6231376eb10d1fb68c60da106ffa0b520b3129471c466eff0304cc", - "sha256:fdafb93bfd5532b13a53c4090675bcd31724160017ecc73e492dc1211bc0377a", - "sha256:fe70d0134fe2115c08866f0cac0eb5c0788093872b5026eb438a74e1ebafd659", - "sha256:ff3ddedaa95284f4f22a92b362f658f3d92f272d8c0fa009051bd5490c4d5a04" - ], - "version": "==0.5.9" - }, - "ypy-websocket": { - "hashes": [ - "sha256:491b2cc4271df4dde9be83017c15f4532b597dc43148472eb20c5aeb838a5b46", - "sha256:9049d5a7d61c26c2b5a39757c9ffcbe2274bf3553adeea8de7fe1c04671d4145" - ], - "markers": "python_version >= '3.7'", - "version": "==0.8.2" - }, "zipp": { "hashes": [ "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b", @@ -1918,19 +1776,19 @@ }, "asgiref": { "hashes": [ - "sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac", - "sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506" + "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e", + "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed" ], "markers": "python_version >= '3.7'", - "version": "==3.6.0" + "version": "==3.7.2" }, "certifi": { "hashes": [ - "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3", - "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18" + "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", + "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" ], "markers": "python_version >= '3.6'", - "version": "==2022.12.7" + "version": "==2023.5.7" }, "cffi": { "hashes": [ @@ -2084,44 +1942,44 @@ }, "cryptography": { "hashes": [ - "sha256:05dc219433b14046c476f6f09d7636b92a1c3e5808b9a6536adf4932b3b2c440", - "sha256:0dcca15d3a19a66e63662dc8d30f8036b07be851a8680eda92d079868f106288", - "sha256:142bae539ef28a1c76794cca7f49729e7c54423f615cfd9b0b1fa90ebe53244b", - "sha256:3daf9b114213f8ba460b829a02896789751626a2a4e7a43a28ee77c04b5e4958", - "sha256:48f388d0d153350f378c7f7b41497a54ff1513c816bcbbcafe5b829e59b9ce5b", - "sha256:4df2af28d7bedc84fe45bd49bc35d710aede676e2a4cb7fc6d103a2adc8afe4d", - "sha256:4f01c9863da784558165f5d4d916093737a75203a5c5286fde60e503e4276c7a", - "sha256:7a38250f433cd41df7fcb763caa3ee9362777fdb4dc642b9a349721d2bf47404", - "sha256:8f79b5ff5ad9d3218afb1e7e20ea74da5f76943ee5edb7f76e56ec5161ec782b", - "sha256:956ba8701b4ffe91ba59665ed170a2ebbdc6fc0e40de5f6059195d9f2b33ca0e", - "sha256:a04386fb7bc85fab9cd51b6308633a3c271e3d0d3eae917eebab2fac6219b6d2", - "sha256:a95f4802d49faa6a674242e25bfeea6fc2acd915b5e5e29ac90a32b1139cae1c", - "sha256:adc0d980fd2760c9e5de537c28935cc32b9353baaf28e0814df417619c6c8c3b", - "sha256:aecbb1592b0188e030cb01f82d12556cf72e218280f621deed7d806afd2113f9", - "sha256:b12794f01d4cacfbd3177b9042198f3af1c856eedd0a98f10f141385c809a14b", - "sha256:c0764e72b36a3dc065c155e5b22f93df465da9c39af65516fe04ed3c68c92636", - "sha256:c33c0d32b8594fa647d2e01dbccc303478e16fdd7cf98652d5b3ed11aa5e5c99", - "sha256:cbaba590180cba88cb99a5f76f90808a624f18b169b90a4abb40c1fd8c19420e", - "sha256:d5a1bd0e9e2031465761dfa920c16b0065ad77321d8a8c1f5ee331021fda65e9" + "sha256:0ddaee209d1cf1f180f1efa338a68c4621154de0afaef92b89486f5f96047c55", + "sha256:14754bcdae909d66ff24b7b5f166d69340ccc6cb15731670435efd5719294895", + "sha256:344c6de9f8bda3c425b3a41b319522ba3208551b70c2ae00099c205f0d9fd3be", + "sha256:34d405ea69a8b34566ba3dfb0521379b210ea5d560fafedf9f800a9a94a41928", + "sha256:3680248309d340fda9611498a5319b0193a8dbdb73586a1acf8109d06f25b92d", + "sha256:3c5ef25d060c80d6d9f7f9892e1d41bb1c79b78ce74805b8cb4aa373cb7d5ec8", + "sha256:4ab14d567f7bbe7f1cdff1c53d5324ed4d3fc8bd17c481b395db224fb405c237", + "sha256:5c1f7293c31ebc72163a9a0df246f890d65f66b4a40d9ec80081969ba8c78cc9", + "sha256:6b71f64beeea341c9b4f963b48ee3b62d62d57ba93eb120e1196b31dc1025e78", + "sha256:7d92f0248d38faa411d17f4107fc0bce0c42cae0b0ba5415505df72d751bf62d", + "sha256:8362565b3835ceacf4dc8f3b56471a2289cf51ac80946f9087e66dc283a810e0", + "sha256:84a165379cb9d411d58ed739e4af3396e544eac190805a54ba2e0322feb55c46", + "sha256:88ff107f211ea696455ea8d911389f6d2b276aabf3231bf72c8853d22db755c5", + "sha256:9f65e842cb02550fac96536edb1d17f24c0a338fd84eaf582be25926e993dde4", + "sha256:a4fc68d1c5b951cfb72dfd54702afdbbf0fb7acdc9b7dc4301bbf2225a27714d", + "sha256:b7f2f5c525a642cecad24ee8670443ba27ac1fab81bba4cc24c7b6b41f2d0c75", + "sha256:b846d59a8d5a9ba87e2c3d757ca019fa576793e8758174d3868aecb88d6fc8eb", + "sha256:bf8fc66012ca857d62f6a347007e166ed59c0bc150cefa49f28376ebe7d992a2", + "sha256:f5d0bf9b252f30a31664b6f64432b4730bb7038339bd18b1fafe129cfc2be9be" ], - "markers": "python_version >= '3.6'", - "version": "==40.0.2" + "markers": "python_version >= '3.7'", + "version": "==41.0.0" }, "deprecated": { "hashes": [ - "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d", - "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d" + "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c", + "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.2.13" + "version": "==1.2.14" }, "django": { "hashes": [ - "sha256:ad33ed68db9398f5dfb33282704925bce044bef4261cd4fb59e4e7f9ae505a78", - "sha256:c36e2ab12824e2ac36afa8b2515a70c53c7742f0d6eaefa7311ec379558db997" + "sha256:066b6debb5ac335458d2a713ed995570536c8b59a580005acb0732378d5eb1ee", + "sha256:7efa6b1f781a6119a10ac94b4794ded90db8accbe7802281cd26f8664ffed59c" ], "index": "pypi", - "version": "==4.2" + "version": "==4.2.1" }, "django-filter": { "hashes": [ @@ -2133,11 +1991,11 @@ }, "django-oauth-toolkit": { "hashes": [ - "sha256:46890decb24a34e2a5382debeaf7752e50d90b7a11716cf2a9fd067097ec0963", - "sha256:abd85c74af525a62365ec2049113e73a2ff8b46ef906e7104a7ba968ef02a11d" + "sha256:47dfeab97ec21496f307c2cf3468e64ca08897fa499bf3104366d32005c9111d", + "sha256:cf1cb1a5744672e6bd7d66b4a110a463bcef9cf5ed4f27e29682cc6a4d0df1ed" ], "index": "pypi", - "version": "==2.2.0" + "version": "==2.3.0" }, "graphene": { "hashes": [ @@ -2149,11 +2007,11 @@ }, "graphene-django": { "hashes": [ - "sha256:60d3ff80ef56ff04bdbee25def56a0b682132f67bbdc1d46cb3e1bfba1528944", - "sha256:d12f60563336b6f12a73acc7dc7bed366262ae0bd796b01119037076c170f20c" + "sha256:6df689056b2d3599e8ca5c96e7cd185927df307b5d3e817fdeedd00d40a52bbc", + "sha256:7d9215e952849d7c97984be0e4cfd0c35c32407bd96eedd13231a22b8f6e8e8a" ], "index": "pypi", - "version": "==3.0.1" + "version": "==3.1.1" }, "graphql-core": { "hashes": [ @@ -2181,10 +2039,10 @@ }, "jwcrypto": { "hashes": [ - "sha256:80a35e9ed1b3b2c43ce03d92c5d48e6d0b6647e2aa2618e4963448923d78a37b" + "sha256:2c1dc51cf8e38ddf324795dfe9426dee9dd46caf47f535ccbc18781fba810b8d" ], "markers": "python_version >= '3.6'", - "version": "==1.4.2" + "version": "==1.5.0" }, "oauthlib": { "hashes": [ @@ -2209,11 +2067,11 @@ }, "requests": { "hashes": [ - "sha256:e8f3c9be120d3333921d213eef078af392fba3933ab7ed2d1cba3b56f2568c3b", - "sha256:f2e34a75f4749019bb0e3effb66683630e4ffeaf75819fb51bebef1bf5aef059" + "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f", + "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" ], "markers": "python_version >= '3.7'", - "version": "==2.29.0" + "version": "==2.31.0" }, "six": { "hashes": [ @@ -2240,11 +2098,11 @@ }, "urllib3": { "hashes": [ - "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305", - "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42" + "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc", + "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.15" + "markers": "python_version >= '3.7'", + "version": "==2.0.2" }, "wrapt": { "hashes": [ diff --git a/django_ledger/__init__.py b/django_ledger/__init__.py index 1192b566..6c95279b 100644 --- a/django_ledger/__init__.py +++ b/django_ledger/__init__.py @@ -9,7 +9,7 @@ default_app_config = 'django_ledger.apps.DjangoLedgerConfig' """Django Ledger""" -__version__ = '0.5.3.1' +__version__ = '0.5.3.2' __license__ = 'GPLv3 License' __author__ = 'Miguel Sanda' diff --git a/django_ledger/models/bill.py b/django_ledger/models/bill.py index ec801bf1..077b1c1c 100644 --- a/django_ledger/models/bill.py +++ b/django_ledger/models/bill.py @@ -35,8 +35,9 @@ from django.utils.translation import gettext_lazy as _ from django_ledger.models.entity import EntityModel -from django_ledger.models.items import ItemTransactionModelQuerySet, ItemTransactionModel -from django_ledger.models.mixins import CreateUpdateMixIn, AccrualMixIn, MarkdownNotesMixIn, PaymentTermsMixIn +from django_ledger.models.items import ItemTransactionModelQuerySet, ItemTransactionModel, ItemModel, ItemModelQuerySet +from django_ledger.models.mixins import (CreateUpdateMixIn, AccrualMixIn, MarkdownNotesMixIn, + PaymentTermsMixIn, ItemizeMixIn) from django_ledger.models.utils import lazy_loader from django_ledger.settings import (DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING, DJANGO_LEDGER_BILL_NUMBER_PREFIX) @@ -242,6 +243,7 @@ def for_entity(self, entity_slug, user_model) -> BillModelQuerySet: class BillModelAbstract(AccrualMixIn, + ItemizeMixIn, PaymentTermsMixIn, MarkdownNotesMixIn, CreateUpdateMixIn): @@ -488,19 +490,29 @@ def configure(self, return self.ledger, self - # State.. - def get_migrate_state_desc(self) -> str: - """ - Description used when migrating transactions into the LedgerModel. + # ### ItemizeMixIn implementation START... + def can_migrate_itemtxs(self) -> bool: + return self.is_draft() - Returns - _______ - str - Description as a string. - """ - return f'Bill {self.bill_number} account adjustment.' + def migrate_itemtxs(self, itemtxs: Dict, operation: str, commit: bool = False): + itemtxs_batch = super().migrate_itemtxs(itemtxs=itemtxs, commit=commit, operation=operation) + self.update_amount_due(itemtxs_qs=itemtxs_batch) + self.get_state(commit=True) + + if commit: + self.save(update_fields=['amount_due', + 'amount_receivable', + 'amount_unearned', + 'amount_earned', + 'updated']) + return itemtxs_batch + + def get_item_model_qs(self) -> ItemModelQuerySet: + return ItemModel.objects.filter( + entity_id__exact=self.ledger.entity_id + ).bills() - def validate_item_transaction_qs(self, queryset: Union[ItemTransactionModelQuerySet, List[ItemTransactionModel]]): + def validate_itemtxs_qs(self, queryset: Union[ItemTransactionModelQuerySet, List[ItemTransactionModel]]): """ Validates that the entire ItemTransactionModelQuerySet is bound to the BillModel. @@ -518,7 +530,7 @@ def validate_item_transaction_qs(self, queryset: Union[ItemTransactionModelQuery def get_itemtxs_data(self, queryset: Optional[ItemTransactionModelQuerySet] = None, aggregate_on_db: bool = False, - ) -> Tuple[ItemTransactionModelQuerySet, Dict]: + lazy_agg: bool = False) -> Tuple[ItemTransactionModelQuerySet, Dict]: """ Fetches the BillModel Items and aggregates the QuerySet. @@ -539,7 +551,7 @@ def get_itemtxs_data(self, 'po_model', 'bill_model') else: - self.validate_item_transaction_qs(queryset) + self.validate_itemtxs_qs(queryset) if aggregate_on_db and isinstance(queryset, ItemTransactionModelQuerySet): return queryset, queryset.aggregate( @@ -549,7 +561,21 @@ def get_itemtxs_data(self, return queryset, { 'total_amount__sum': sum(i.total_amount for i in queryset), 'total_items': len(queryset) - } + } if not lazy_agg else None + + # ### ItemizeMixIn implementation END... + + # State.. + def get_migrate_state_desc(self) -> str: + """ + Description used when migrating transactions into the LedgerModel. + + Returns + _______ + str + Description as a string. + """ + return f'Bill {self.bill_number} account adjustment.' def get_migration_data(self, queryset: Optional[ItemTransactionModelQuerySet] = None) -> ItemTransactionModelQuerySet: @@ -565,7 +591,7 @@ def get_migration_data(self, if not queryset: queryset = self.itemtransactionmodel_set.all() else: - self.validate_item_transaction_qs(queryset) + self.validate_itemtxs_qs(queryset) return queryset.order_by('item_model__expense_account__uuid', 'entity_unit__uuid', @@ -601,7 +627,6 @@ def update_amount_due(self, self.amount_due = round(itemtxs_agg['total_amount__sum'], 2) return itemtxs_qs - # State def is_draft(self) -> bool: """ Checks if the BillModel is in Draft status. @@ -1014,7 +1039,7 @@ def mark_as_review(self, if not itemtxs_qs: itemtxs_qs = self.itemtransactionmodel_set.all() else: - self.validate_item_transaction_qs(queryset=itemtxs_qs) + self.validate_itemtxs_qs(queryset=itemtxs_qs) if not itemtxs_qs.count(): raise BillModelValidationError(message=f'Cannot review a {self.__class__.__name__} without items...') @@ -1117,7 +1142,7 @@ def mark_as_approved(self, ) self.bill_status = self.BILL_STATUS_APPROVED self.date_approved = localdate() if not date_approved else date_approved - self.new_state(commit=True) + self.get_state(commit=True) self.clean() if commit: self.save(update_fields=[ @@ -1226,13 +1251,13 @@ def mark_as_paid(self, f'Cannot pay {self.__class__.__name__} before approved date {self.date_approved}.') self.bill_status = self.BILL_STATUS_PAID - self.new_state(commit=True) + self.get_state(commit=True) self.clean() if not itemtxs_qs: itemtxs_qs = self.itemtransactionmodel_set.all() else: - self.validate_item_transaction_qs(queryset=itemtxs_qs) + self.validate_itemtxs_qs(queryset=itemtxs_qs) if commit: self.save(update_fields=[ diff --git a/django_ledger/models/entity.py b/django_ledger/models/entity.py index e70df187..3c0569b6 100644 --- a/django_ledger/models/entity.py +++ b/django_ledger/models/entity.py @@ -1234,6 +1234,10 @@ def create_bill(self, return bill_model + def get_items_for_bill(self) -> ItemModelQuerySet: + item_model_qs: ItemModelQuerySet = self.itemmodel_set.all() + return item_model_qs.select_related('uom', 'entity').bills() + # ### INVOICE MANAGEMENT #### def get_invoices(self): """ diff --git a/django_ledger/models/estimate.py b/django_ledger/models/estimate.py index 8c74742b..2d29a681 100644 --- a/django_ledger/models/estimate.py +++ b/django_ledger/models/estimate.py @@ -16,7 +16,7 @@ from datetime import date from decimal import Decimal from string import ascii_uppercase, digits -from typing import Union, Optional, List +from typing import Union, Optional, List, Dict from uuid import uuid4, UUID from django.contrib.auth import get_user_model @@ -33,8 +33,8 @@ from django_ledger.models import BillModelQuerySet, InvoiceModelQuerySet from django_ledger.models.customer import CustomerModel from django_ledger.models.entity import EntityModel, EntityStateModel -from django_ledger.models.items import ItemTransactionModelQuerySet, ItemTransactionModel -from django_ledger.models.mixins import CreateUpdateMixIn, MarkdownNotesMixIn +from django_ledger.models.items import ItemTransactionModelQuerySet, ItemTransactionModel, ItemModelQuerySet, ItemModel +from django_ledger.models.mixins import CreateUpdateMixIn, MarkdownNotesMixIn, ItemizeMixIn from django_ledger.models.purchase_order import PurchaseOrderModelQuerySet from django_ledger.settings import DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING, DJANGO_LEDGER_ESTIMATE_NUMBER_PREFIX @@ -102,6 +102,9 @@ def estimates(self): """ return self.not_approved() + def draft(self): + return self.filter(status__exact=EstimateModelAbstract.CONTRACT_STATUS_DRAFT) + class EstimateModelManager(models.Manager): """ @@ -147,7 +150,9 @@ def for_entity(self, entity_slug: Union[EntityModel, str], user_model): ) -class EstimateModelAbstract(CreateUpdateMixIn, MarkdownNotesMixIn): +class EstimateModelAbstract(CreateUpdateMixIn, + ItemizeMixIn, + MarkdownNotesMixIn): """ This is the main abstract class which the EstimateModel database will inherit from. The EstimateModel inherits functionality from the following MixIns: @@ -1000,17 +1005,58 @@ def get_mark_as_void_message(self): def get_html_id(self): return f'djl-customer-estimate-id-{self.uuid}' - # ItemThroughModels... + # ### ItemizeMixIn implementation START... + def can_migrate_itemtxs(self) -> bool: + return self.is_draft() + + def migrate_itemtxs(self, itemtxs: Dict, operation: str, commit: bool = False): + itemtxs_batch = super().migrate_itemtxs(itemtxs=itemtxs, commit=commit, operation=operation) + self.update_state(itemtxs_qs=itemtxs_batch) + self.clean() + + if commit: + self.save(update_fields=[ + 'revenue_estimate', + 'labor_estimate', + 'equipment_estimate', + 'material_estimate', + 'other_estimate', + 'updated' + ]) + + return itemtxs_batch + + def get_item_model_qs(self) -> ItemModelQuerySet: + return ItemModel.objects.filter( + entity_id__exact=self.entity_id + ).estimates() + + def validate_itemtxs_qs(self, queryset: Union[ItemTransactionModelQuerySet, List[ItemTransactionModel]]): + """ + Validates that the entire ItemTransactionModelQuerySet is bound to the EstimateModel. + + Parameters + ---------- + queryset: ItemTransactionModelQuerySet or list of ItemTransactionModel. + ItemTransactionModelQuerySet to validate. + """ + valid = all([ + i.ce_model_id == self.uuid for i in queryset + ]) + if not valid: + raise EstimateModelValidationError(f'Invalid queryset. All items must be assigned to Bill {self.uuid}') + def get_itemtxs_data(self, - itemtxs_qs: Optional[Union[ItemTransactionModelQuerySet, List[ItemTransactionModel]]] = None - ) -> ItemTransactionModelQuerySet: - # todo: this needs to return an aggregate for consistency... + queryset: Optional[Union[ItemTransactionModelQuerySet, List[ItemTransactionModel]]] = None, + aggregate_on_db: bool = False, + lazy_agg: bool = False): + """ Returns all ItemTransactionModels associated with the EstimateModel and a total aggregate. Parameters ---------- - itemtxs_qs: ItemTransactionModelQuerySet + queryset: ItemTransactionModelQuerySet ItemTransactionModelQuerySet to use. Avoids additional DB query if provided. Validated if provided. @@ -1018,13 +1064,14 @@ def get_itemtxs_data(self, ------- ItemTransactionModelQuerySet """ - if not itemtxs_qs: - itemtxs_qs = self.itemtransactionmodel_set.select_related('item_model').all() + if not queryset: + queryset = self.itemtransactionmodel_set.select_related('item_model').all() else: - self.validate_item_transaction_qs(itemtxs_qs) - - return itemtxs_qs + self.validate_item_transaction_qs(queryset) + # todo: this needs to return an aggregate for consistency... + return queryset, None + # ### ItemizeMixIn implementation END... def get_itemtxs_annotation(self, itemtxs_qs: Optional[ItemTransactionModelQuerySet] = None): """ Gets an annotated ItemTransactionModelQuerySet with additional average unit cost & revenue. @@ -1041,7 +1088,7 @@ def get_itemtxs_annotation(self, itemtxs_qs: Optional[ItemTransactionModelQueryS tuple The original ItemTransactionModelQuerySet and the annotated ItemTransactionModelQuerySet. """ - itemtxs_qs = self.get_itemtxs_data(itemtxs_qs) + itemtxs_qs, _ = self.get_itemtxs_data(itemtxs_qs) return itemtxs_qs, itemtxs_qs.values( 'item_model_id', 'item_model__name' ).annotate( @@ -1071,7 +1118,7 @@ def update_revenue_estimate(self, itemtxs_qs: Optional[ItemTransactionModelQuery commit: bool If True, the new revenue estimate will be committed into the DB. """ - itemtxs_qs = self.get_itemtxs_data(itemtxs_qs) + itemtxs_qs, _ = self.get_itemtxs_data(itemtxs_qs) self.revenue_estimate = sum(i.ce_revenue_estimate for i in itemtxs_qs) if commit: @@ -1092,7 +1139,7 @@ def update_cost_estimate(self, itemtxs_qs: Optional[ItemTransactionModelQuerySet commit: bool If True, the new revenue estimate will be committed into the DB. """ - itemtxs_qs = self.get_itemtxs_data(itemtxs_qs=itemtxs_qs) + itemtxs_qs, _ = self.get_itemtxs_data(queryset=itemtxs_qs) estimates = { 'labor': sum(a.ce_cost_estimate for a in itemtxs_qs if a.item_model.is_labor()), 'material': sum(a.ce_cost_estimate for a in itemtxs_qs if a.item_model.is_material()), @@ -1119,7 +1166,7 @@ def update_cost_estimate(self, itemtxs_qs: Optional[ItemTransactionModelQuerySet def update_state(self, itemtxs_qs: Optional[Union[ItemTransactionModelQuerySet, List[ItemTransactionModel]]] = None): - itemtxs_qs = self.get_itemtxs_data(itemtxs_qs=itemtxs_qs) + itemtxs_qs, _ = self.get_itemtxs_data(queryset=itemtxs_qs) self.update_cost_estimate(itemtxs_qs) self.update_revenue_estimate(itemtxs_qs) diff --git a/django_ledger/models/invoice.py b/django_ledger/models/invoice.py index 13396574..971374de 100644 --- a/django_ledger/models/invoice.py +++ b/django_ledger/models/invoice.py @@ -28,16 +28,17 @@ from django.contrib.auth import get_user_model from django.core.exceptions import ValidationError, ObjectDoesNotExist from django.db import models, transaction, IntegrityError -from django.db.models import Q, Sum, F +from django.db.models import Q, Sum, F, Count from django.db.models.signals import post_delete, pre_save from django.shortcuts import get_object_or_404 from django.urls import reverse from django.utils.timezone import localdate from django.utils.translation import gettext_lazy as _ -from django_ledger.models import lazy_loader, ItemTransactionModelQuerySet +from django_ledger.models import lazy_loader, ItemTransactionModelQuerySet, ItemModelQuerySet, ItemModel from django_ledger.models.entity import EntityModel -from django_ledger.models.mixins import CreateUpdateMixIn, AccrualMixIn, MarkdownNotesMixIn, PaymentTermsMixIn +from django_ledger.models.mixins import CreateUpdateMixIn, AccrualMixIn, MarkdownNotesMixIn, PaymentTermsMixIn, \ + ItemizeMixIn from django_ledger.settings import DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING, DJANGO_LEDGER_INVOICE_NUMBER_PREFIX UserModel = get_user_model() @@ -202,6 +203,7 @@ def for_entity_unpaid(self, entity_slug, user_model): class InvoiceModelAbstract(AccrualMixIn, + ItemizeMixIn, PaymentTermsMixIn, MarkdownNotesMixIn, CreateUpdateMixIn): @@ -439,18 +441,30 @@ def configure(self, self.save() return self.ledger, self - def get_migrate_state_desc(self): - """ - Description used when migrating transactions into the LedgerModel. + # ### ItemizeMixIn implementation START... - Returns - _______ - str - Description as a string. - """ - return f'Invoice {self.invoice_number} account adjustment.' + def can_migrate_itemtxs(self) -> bool: + return self.is_draft() + + def migrate_itemtxs(self, itemtxs: Dict, operation: str, commit: bool = False): + itemtxs_batch = super().migrate_itemtxs(itemtxs=itemtxs, commit=commit, operation=operation) + self.update_amount_due(itemtxs_qs=itemtxs_batch) + self.get_state(commit=True) + + if commit: + self.save(update_fields=['amount_due', + 'amount_receivable', + 'amount_unearned', + 'amount_earned', + 'updated']) + return itemtxs_batch + + def get_item_model_qs(self) -> ItemModelQuerySet: + return ItemModel.objects.filter( + entity_id__exact=self.ledger.entity_id + ).invoices() - def validate_item_transaction_qs(self, queryset: ItemTransactionModelQuerySet): + def validate_itemtxs_qs(self, queryset: ItemTransactionModelQuerySet): """ Validates that the entire ItemTransactionModelQuerySet is bound to the InvoiceModel. @@ -466,7 +480,10 @@ def validate_item_transaction_qs(self, queryset: ItemTransactionModelQuerySet): raise InvoiceModelValidationError(f'Invalid queryset. All items must be assigned to Invoice {self.uuid}') def get_itemtxs_data(self, - queryset: ItemTransactionModelQuerySet = None) -> Tuple[ItemTransactionModelQuerySet, Dict]: + queryset: ItemTransactionModelQuerySet = None, + aggregate_on_db: bool = False, + lazy_agg: bool = False, + ) -> Tuple[ItemTransactionModelQuerySet, Dict]: """ Fetches the InvoiceModel Items and aggregates the QuerySet. @@ -488,12 +505,31 @@ def get_itemtxs_data(self, 'invoice_model' ) else: - self.validate_item_transaction_qs(queryset) + self.validate_itemtxs_qs(queryset) + + if aggregate_on_db and isinstance(queryset, ItemTransactionModelQuerySet): + return queryset, queryset.aggregate( + total_amount__sum=Sum('total_amount'), + total_items=Count('uuid') + ) return queryset, { 'total_amount__sum': sum(i.total_amount for i in queryset), 'total_items': len(queryset) - } + } if not lazy_agg else None + + # ### ItemizeMixIn implementation END... + + def get_migrate_state_desc(self): + """ + Description used when migrating transactions into the LedgerModel. + + Returns + _______ + str + Description as a string. + """ + return f'Invoice {self.invoice_number} account adjustment.' def get_migration_data(self, queryset: Optional[ItemTransactionModelQuerySet] = None) -> ItemTransactionModelQuerySet: @@ -509,7 +545,7 @@ def get_migration_data(self, if not queryset: queryset = self.itemtransactionmodel_set.all() else: - self.validate_item_transaction_qs(queryset) + self.validate_itemtxs_qs(queryset) return queryset.select_related('item_model').order_by('item_model__earnings_account__uuid', 'entity_unit__uuid', @@ -1107,7 +1143,7 @@ def mark_as_paid(self, if self.date_paid > localdate(): raise InvoiceModelValidationError(f'Cannot pay {self.__class__.__name__} in the future.') - self.new_state(commit=True) + self.get_state(commit=True) self.invoice_status = self.INVOICE_STATUS_PAID self.clean() diff --git a/django_ledger/models/items.py b/django_ledger/models/items.py index 9200c89e..a2bfe5ba 100644 --- a/django_ledger/models/items.py +++ b/django_ledger/models/items.py @@ -264,6 +264,32 @@ def inventory_all(self): ) ) + def bills(self): + """ + Filters the QuerySet to ItemModels that are eligible only for bills.. + + Returns + ------- + ItemModelQuerySet + A Filtered ItemModelQuerySet. + """ + return self.filter( + ( + Q(is_product_or_service=False) & + Q(for_inventory=False) + ) | + Q(for_inventory=True) + ) + + def invoices(self): + return self.filter(is_product_or_service=True) + + def estimates(self): + return self.invoices() + + def purchase_orders(self): + return self.inventory_all() + class ItemModelManager(models.Manager): """ @@ -480,6 +506,9 @@ class ItemModelAbstract(CreateUpdateMixIn): uuid = models.UUIDField(default=uuid4, editable=False, primary_key=True) name = models.CharField(max_length=100, verbose_name=_('Item Name')) + # todo: rename this and remove 'id' from it. + item_id = models.CharField(max_length=50, blank=True, null=True, verbose_name=_('Internal ID')) + item_number = models.CharField(max_length=30, editable=False, verbose_name=_('Item Number')) item_role = models.CharField(max_length=10, choices=ITEM_ROLE_CHOICES, null=True, blank=True) item_type = models.CharField(max_length=1, choices=ITEM_TYPE_CHOICES, null=True, blank=True) @@ -490,9 +519,6 @@ class ItemModelAbstract(CreateUpdateMixIn): sku = models.CharField(max_length=50, blank=True, null=True, verbose_name=_('SKU Code')) upc = models.CharField(max_length=50, blank=True, null=True, verbose_name=_('UPC Code')) - # todo: rename this and remove 'id' from it. - item_id = models.CharField(max_length=50, blank=True, null=True, verbose_name=_('Internal ID')) - item_number = models.CharField(max_length=30, editable=False, verbose_name=_('Item Number')) is_active = models.BooleanField(default=True, verbose_name=_('Is Active')) default_amount = models.DecimalField(max_digits=20, @@ -1368,4 +1394,4 @@ class ItemTransactionModel(ItemTransactionModelAbstract): class ItemModel(ItemModelAbstract): """ Base ItemModel from Abstract. - """ \ No newline at end of file + """ diff --git a/django_ledger/models/mixins.py b/django_ledger/models/mixins.py index 03bd80c8..e4699540 100644 --- a/django_ledger/models/mixins.py +++ b/django_ledger/models/mixins.py @@ -13,7 +13,7 @@ from datetime import timedelta, date, datetime from decimal import Decimal from itertools import groupby -from typing import Optional, Union, Dict +from typing import Optional, Union, Dict, List from uuid import UUID from django.conf import settings @@ -682,7 +682,7 @@ def migrate_state(self, } if not void: - new_state = self.new_state(commit=commit) + new_state = self.get_state(commit=commit) else: new_state = self.void_state(commit=commit) @@ -818,7 +818,7 @@ def void_state(self, commit: bool = False) -> Dict: self.update_state(void_state) return void_state - def new_state(self, commit: bool = False): + def get_state(self, commit: bool = False): """ Determines the new state of the financial instrument based on progress. @@ -852,7 +852,7 @@ def update_state(self, state: Optional[Dict] = None): Optional user provided state to use. """ if not state: - state = self.new_state() + state = self.get_state() self.amount_paid = abs(state['amount_paid']) self.amount_receivable = state['amount_receivable'] self.amount_unearned = state['amount_unearned'] @@ -1190,3 +1190,174 @@ def send_log(self, msg, level, force): if self.LOGGER_BYPASS_DEBUG or settings.DEBUG or force: logger = self.get_logger() logger.log(msg=msg, level=level) + + +class ItemTransactionQuerySet: + pass + + +class ItemizeError(ValidationError): + pass + + +class ItemizeMixIn: + ITEMIZE_APPEND = 'append' + ITEMIZE_REPLACE = 'replace' + ITEMIZE_UPDATE = 'update' + + def get_item_model_qs(self): + """ + Fetches the ItemModelQuerySet eligible to itemize. + + Returns + ------- + ItemModelQuerySet + """ + raise NotImplementedError() + + def get_itemtxs_data(self, queryset=None, aggregate_on_db: bool = False, lazy_agg: bool = False): + """ + Fetches the ItemTransactionModelQuerySet associated with the model. + + Parameters + ---------- + queryset: ItemTransactionModelQuerySet + Pre-fetched ItemTransactionModelQuerySet. Validated if provided. + aggregate_on_db: bool + If True, performs aggregation at the DB layer. Defaults to False. + lazy_agg: bool + If True, performs queryset aggregation metrics. Defaults to False. + + Returns + ------- + tuple + ItemModelQuerySet, dict + """ + raise NotImplementedError() + + def validate_itemtxs(self, itemtxs): + """ + Validates the provided item transaction list. + + Parameters + ---------- + itemtxs: dict + Item transaction list to replace/aggregate. + """ + if isinstance(itemtxs, dict): + if all([ + all([ + isinstance(d, dict), + 'unit_cost' in d, + 'quantity' in d, + 'total_amount' in d + ]) for i, d in itemtxs.items() + ]): + return + raise ItemizeError('itemtxs must be an instance of dict.') + + def can_migrate_itemtxs(self) -> bool: + """ + Checks if item transaction list can be migrated. + + Returns + ------- + bool + """ + raise NotImplementedError() + + def _get_itemtxs_batch(self, itemtxs): + ItemTransactionModel = lazy_loader.get_item_transaction_model() + EstimateModel = lazy_loader.get_estimate_model() + PurchaseOrder = lazy_loader.get_purchase_order_model() + + item_model_qs = self.get_item_model_qs() + item_model_qs = item_model_qs.filter(item_number__in=itemtxs.keys()) + item_model_qs_map = {i.item_number: i for i in item_model_qs} + + if itemtxs.keys() != item_model_qs_map.keys(): + raise ItemizeError(message=f'Got items {itemtxs.keys()}, but only {item_model_qs_map.keys()} exists.') + + if isinstance(self, EstimateModel): + return [ + ItemTransactionModel( + ce_model=self, + item_model=item_model_qs_map[item_number], + ce_quantity=i['quantity'], + ce_unit_cost_estimate=i['unit_cost'], + ce_unit_revenue_estimate=i['unit_revenue'], + ) for item_number, i in itemtxs.items() + ] + + if isinstance(self, PurchaseOrder): + return [ + ItemTransactionModel( + po_model=self, + item_model=item_model_qs_map[item_number], + po_quantity=i['quantity'], + po_unit_cost=i['unit_cost'], + ) for item_number, i in itemtxs.items() + ] + + BillModel = lazy_loader.get_bill_model() + InvoiceModel = lazy_loader.get_invoice_model() + + return [ + ItemTransactionModel( + bill_model=self if isinstance(self, BillModel) else None, + invoice_model=self if isinstance(self, InvoiceModel) else None, + item_model=item_model_qs_map[item_number], + quantity=i['quantity'], + unit_cost=i['unit_cost'] + ) for item_number, i in itemtxs.items() + ] + + def migrate_itemtxs(self, itemtxs: Dict, operation: str, commit: bool = False): + """ + Migrates a predefined item transaction list. + + Parameters + ---------- + itemtxs: dict + A dictionary where keys are the document number (invoice/bill number, etc) and values are a dictionary: + operation: str + A choice of ITEMIZE_REPLACE, ITEMIZE_APPEND, ITEMIZE_UPDATE + commit: bool + If True, commits transaction into the DB. Default to False + + Returns + ------- + list + A list of ItemTransactionModel appended or created. + """ + if operation == self.ITEMIZE_UPDATE: + raise NotImplementedError(f'Operation {operation} not yet implemented.') + + if self.can_migrate_itemtxs(): + self.validate_itemtxs(itemtxs) + + itemtxs_batch = self._get_itemtxs_batch(itemtxs) + + for itx in itemtxs_batch: + itx.clean_fields() + itx.clean() + + if commit: + + ItemTransactionModel = lazy_loader.get_item_transaction_model() + + if operation == self.ITEMIZE_APPEND: + ItemTransactionModel.objects.bulk_create(objs=itemtxs_batch) + itemtxs_qs, _ = self.get_itemtxs_data(lazy_agg=True) + return itemtxs_qs + elif operation == self.ITEMIZE_REPLACE: + itemtxs_qs, _ = self.get_itemtxs_data(lazy_agg=True) + itemtxs_qs.delete() + return ItemTransactionModel.objects.bulk_create(objs=itemtxs_batch) + return itemtxs_batch + + def validate_itemtxs_qs(self): + """ + Validates that the provided item transaction list is valid. + """ + raise NotImplementedError() diff --git a/django_ledger/models/purchase_order.py b/django_ledger/models/purchase_order.py index 6a4f8856..4a8d405d 100644 --- a/django_ledger/models/purchase_order.py +++ b/django_ledger/models/purchase_order.py @@ -18,7 +18,7 @@ """ from datetime import date from string import ascii_uppercase, digits -from typing import Tuple, List, Union, Optional +from typing import Tuple, List, Union, Optional, Dict from uuid import uuid4 from django.contrib.auth import get_user_model @@ -35,8 +35,8 @@ from django_ledger.models.bill import BillModel, BillModelQuerySet from django_ledger.models.entity import EntityModel -from django_ledger.models.items import ItemTransactionModel, ItemTransactionModelQuerySet -from django_ledger.models.mixins import CreateUpdateMixIn, MarkdownNotesMixIn +from django_ledger.models.items import ItemTransactionModel, ItemTransactionModelQuerySet, ItemModelQuerySet, ItemModel +from django_ledger.models.mixins import CreateUpdateMixIn, MarkdownNotesMixIn, ItemizeMixIn from django_ledger.models.utils import lazy_loader from django_ledger.settings import DJANGO_LEDGER_DOCUMENT_NUMBER_PADDING, DJANGO_LEDGER_PO_NUMBER_PREFIX @@ -92,6 +92,9 @@ def active(self): Q(po_status__exact=PurchaseOrderModel.PO_STATUS_FULFILLED) ) + def draft(self): + return self.filter(po_status__exact=PurchaseOrderModel.PO_STATUS_DRAFT) + class PurchaseOrderModelManager(models.Manager): """ @@ -119,7 +122,9 @@ def for_entity(self, entity_slug, user_model) -> PurchaseOrderModelQuerySet: ) -class PurchaseOrderModelAbstract(CreateUpdateMixIn, MarkdownNotesMixIn): +class PurchaseOrderModelAbstract(CreateUpdateMixIn, + ItemizeMixIn, + MarkdownNotesMixIn): """ The base implementation of the PurchaseOrderModel. @@ -332,10 +337,30 @@ def validate_item_transaction_qs(self, queryset: Union[ItemTransactionModelQuery if not valid: raise PurchaseOrderModelValidationError(f'Invalid queryset. All items must be assigned to PO {self.uuid}') - # State Update... + # ### ItemizeMixIn implementation START... + + def can_migrate_itemtxs(self) -> bool: + return self.is_draft() + + def migrate_itemtxs(self, itemtxs: Dict, operation: str, commit: bool = False): + itemtxs_batch = super().migrate_itemtxs(itemtxs=itemtxs, commit=commit, operation=operation) + self.update_state(itemtxs_qs=itemtxs_batch) + self.clean() + if commit: + self.save(update_fields=['po_amount', + 'po_amount_received', + 'updated']) + return itemtxs_batch + + def get_item_model_qs(self) -> ItemModelQuerySet: + return ItemModel.objects.filter( + entity_id__exact=self.entity_id + ).purchase_orders() + def get_itemtxs_data(self, queryset: Optional[Union[ItemTransactionModelQuerySet, List[ItemTransactionModel]]] = None, - aggregate_on_db: bool = False) -> Tuple: + aggregate_on_db: bool = False, + lazy_agg: bool = False) -> Tuple: """ Fetches the PurchaseOrderModel Items and aggregates the QuerySet. @@ -366,8 +391,9 @@ def get_itemtxs_data(self, 'po_total_amount__sum': sum(i.total_amount for i in queryset), 'bill_amount_paid__sum': sum(i.bill_model.amount_paid for i in queryset if i.bill_model_id), 'total_items': len(queryset) - } + } if not lazy_agg else None + # ### ItemizeMixIn implementation END... def update_state(self, itemtxs_qs: Optional[Union[ItemTransactionModelQuerySet, List[ItemTransactionModel]]] = None ) -> Tuple: diff --git a/django_ledger/templates/django_ledger/estimate/estimate_detail.html b/django_ledger/templates/django_ledger/estimate/estimate_detail.html index cd5ede3d..0f8a7034 100644 --- a/django_ledger/templates/django_ledger/estimate/estimate_detail.html +++ b/django_ledger/templates/django_ledger/estimate/estimate_detail.html @@ -16,21 +16,21 @@

{% trans 'Revenue Estimate' %}:

- {% currency_symbol %}{{ estimate_model.revenue_estimate | currency_format }}

+ {% currency_symbol %}{{ estimate_model.get_revenue_estimate | currency_format }}

{% trans 'Cost Estimate' %}:

- {% currency_symbol %}{{ estimate_model.cost_estimate | absolute | currency_format }}

+ {% currency_symbol %}{{ estimate_model.get_cost_estimate | absolute | currency_format }}

{% trans 'Profit Estimate' %}:

- {% currency_symbol %}{{ estimate_model.profit_estimate | currency_format }}

+ {% currency_symbol %}{{ estimate_model.get_profit_estimate | currency_format }}

diff --git a/django_ledger/templates/django_ledger/estimate/includes/card_estimate.html b/django_ledger/templates/django_ledger/estimate/includes/card_estimate.html index 1b0392db..57b8a896 100644 --- a/django_ledger/templates/django_ledger/estimate/includes/card_estimate.html +++ b/django_ledger/templates/django_ledger/estimate/includes/card_estimate.html @@ -12,9 +12,9 @@

{% trans 'Status' %}: {{ estimate_model.get_status_display }}

{% trans 'Estimated Revenue' %}: - {% currency_symbol %}{{ estimate_model.revenue_estimate }}

+ {% currency_symbol %}{{ estimate_model.get_revenue_estimate | currency_format }}

{% trans 'Profit Estimate' %}: - {% currency_symbol %}{{ estimate_model.profit_estimate }}

+ {% currency_symbol %}{{ estimate_model.get_profit_estimate | currency_format }}

{% trans 'Cost Breakdown' %}:

@@ -40,11 +40,11 @@

{% trans 'Cost Progress' %}

+ max="{{ contract_progress.get_cost_estimate }}">

{% trans 'Invoice Progress' %}

+ max="{{ contract_progress.get_revenue_estimate }}">

{% trans 'Received Progress' %}

{% trans 'Received Progress' %} {% modal_action_v2 estimate_model estimate_model.get_mark_as_approved_url estimate_model.get_mark_as_approved_message estimate_model.get_mark_as_approved_html_id %} {% endif %} - {# MARK APPROVE #} - {# {% if estimate_model.can_approve %}#} - {# {% trans 'Mark as Draft' %}#} - {# MARK AS DRAFT MODAL #} - {# {% modal_action_v2 estimate_model estimate_model.get_mark_as_approved_url estimate_model.get_mark_as_approved_message estimate_model.get_mark_as_approved_html_id %}#} - {# {% endif %}#}
diff --git a/django_ledger/templates/django_ledger/estimate/includes/estimate_item_table.html b/django_ledger/templates/django_ledger/estimate/includes/estimate_item_table.html index be615fe9..9263ca07 100644 --- a/django_ledger/templates/django_ledger/estimate/includes/estimate_item_table.html +++ b/django_ledger/templates/django_ledger/estimate/includes/estimate_item_table.html @@ -38,10 +38,10 @@ {% trans 'Total' %} - {% currency_symbol %}{{ ce_model.cost_estimate | currency_format }} + {% currency_symbol %}{{ ce_model.get_cost_estimate | currency_format }} - {% currency_symbol %}{{ ce_model.revenue_estimate | currency_format }} + {% currency_symbol %}{{ ce_model.get_revenue_estimate | currency_format }} diff --git a/django_ledger/templates/django_ledger/invoice/invoice_detail.html b/django_ledger/templates/django_ledger/invoice/invoice_detail.html index 5adbacd7..23468a92 100644 --- a/django_ledger/templates/django_ledger/invoice/invoice_detail.html +++ b/django_ledger/templates/django_ledger/invoice/invoice_detail.html @@ -68,8 +68,8 @@ {% else %}
-

You Still Owe

-

+

You Are Owed

+

{% currency_symbol %}{{ invoice.get_amount_open | currency_format }}

diff --git a/django_ledger/templatetags/django_ledger.py b/django_ledger/templatetags/django_ledger.py index 6a0e40bf..c6f4a84f 100644 --- a/django_ledger/templatetags/django_ledger.py +++ b/django_ledger/templatetags/django_ledger.py @@ -258,7 +258,7 @@ def invoice_txs_table(context, invoice_model: InvoiceModel): invoice_model=invoice_model, user_model=context['request'].user, entity_slug=context['view'].kwargs['entity_slug'] - ).select_related('journal_entry').order_by('-journal_entry__date') + ).select_related('journal_entry').order_by('-journal_entry__timestamp') total_credits = sum(tx.amount for tx in txs_queryset if tx.tx_type == 'credit') total_debits = sum(tx.amount for tx in txs_queryset if tx.tx_type == 'debit') return { diff --git a/django_ledger/views/bill.py b/django_ledger/views/bill.py index cf1f6ae1..beca0831 100644 --- a/django_ledger/views/bill.py +++ b/django_ledger/views/bill.py @@ -170,7 +170,7 @@ def form_valid(self, form): bill_model.ce_model_id = po_model.ce_model_id bill_model.update_amount_due() - bill_model.new_state(commit=True) + bill_model.get_state(commit=True) bill_model.clean() bill_model.save() po_model_items_qs.update(bill_model=bill_model) @@ -450,7 +450,7 @@ def post(self, request, bill_pk, entity_slug, *args, **kwargs): itemtxs_formset.save() itemtxs_qs = bill_model.update_amount_due() - bill_model.new_state(commit=True) + bill_model.get_state(commit=True) bill_model.clean() bill_model.save(update_fields=['amount_due', 'amount_receivable', diff --git a/django_ledger/views/estimate.py b/django_ledger/views/estimate.py index 4e58be91..09ac237a 100644 --- a/django_ledger/views/estimate.py +++ b/django_ledger/views/estimate.py @@ -159,7 +159,7 @@ def get_context_data(self, itemtxs_formset=None, **kwargs): context['header_subtitle'] = ce_model.title context['header_subtitle_icon'] = 'eos-icons:job' if not itemtxs_formset: - itemtxs_qs: ItemTransactionModelQuerySet = ce_model.get_itemtxs_data() + itemtxs_qs, _ = ce_model.get_itemtxs_data() itemtxs_agg = itemtxs_qs.get_estimate_aggregate() if ce_model.can_update_items(): itemtxs_formset = CanEditEstimateItemModelFormset( @@ -176,7 +176,7 @@ def get_context_data(self, itemtxs_formset=None, **kwargs): queryset=itemtxs_qs ) else: - itemtxs_qs = ce_model.get_itemtxs_data(itemtxs_qs=itemtxs_formset.queryset) + itemtxs_qs, _ = ce_model.get_itemtxs_data(queryset=itemtxs_formset.queryset) itemtxs_agg = itemtxs_qs.get_estimate_aggregate() context['ce_revenue_estimate__sum'] = itemtxs_agg['ce_revenue_estimate__sum'] diff --git a/django_ledger/views/invoice.py b/django_ledger/views/invoice.py index dba3d374..8cf036c4 100644 --- a/django_ledger/views/invoice.py +++ b/django_ledger/views/invoice.py @@ -327,7 +327,7 @@ def post(self, request, entity_slug, invoice_pk, *args, **kwargs): itemtxs_list = itemtxs_formset.save() itemtxs_qs = invoice_model.update_amount_due() - invoice_model.new_state(commit=True) + invoice_model.get_state(commit=True) invoice_model.clean() invoice_model.save( update_fields=['amount_due', diff --git a/notebooks/QuickStart Notebook.ipynb b/notebooks/QuickStart Notebook.ipynb index dee49783..dab38d5d 100644 --- a/notebooks/QuickStart Notebook.ipynb +++ b/notebooks/QuickStart Notebook.ipynb @@ -4,13 +4,14 @@ "cell_type": "code", "execution_count": null, "metadata": { - "is_executing": true + "is_executing": true, + "tags": [] }, "outputs": [], "source": [ "import os\n", "from datetime import date, datetime\n", - "from random import randint\n", + "from random import randint, choices, random\n", "\n", "import django\n", "\n", @@ -53,7 +54,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:25:51.456598352Z", "start_time": "2023-05-02T12:25:51.447664279Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -77,7 +79,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:25:52.456383236Z", "start_time": "2023-05-02T12:25:52.433795957Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -110,7 +113,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:25:55.034942055Z", "start_time": "2023-05-02T12:25:55.032364115Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -124,7 +128,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:25:55.592227653Z", "start_time": "2023-05-02T12:25:55.540012210Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -141,7 +146,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:25:56.272229803Z", "start_time": "2023-05-02T12:25:56.265634611Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -169,7 +175,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:25:59.223473672Z", "start_time": "2023-05-02T12:25:59.219511440Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -183,7 +190,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:26:07.661561066Z", "start_time": "2023-05-02T12:26:00.739321371Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -204,7 +212,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:26:38.055549032Z", "start_time": "2023-05-02T12:26:37.999323946Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -218,7 +227,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:26:39.921887978Z", "start_time": "2023-05-02T12:26:39.915563054Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -246,7 +256,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:26:43.009405285Z", "start_time": "2023-05-02T12:26:42.942771183Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -277,7 +288,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:26:44.617468507Z", "start_time": "2023-05-02T12:26:44.579227915Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -292,7 +304,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:26:45.435390661Z", "start_time": "2023-05-02T12:26:45.357464265Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -314,7 +327,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:26:46.584346488Z", "start_time": "2023-05-02T12:26:46.556516411Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -336,7 +350,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:26:47.779311513Z", "start_time": "2023-05-02T12:26:47.704570419Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -358,7 +373,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:26:49.123819554Z", "start_time": "2023-05-02T12:26:49.032005255Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -380,7 +396,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:26:50.730393083Z", "start_time": "2023-05-02T12:26:50.637183861Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -395,7 +412,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:26:56.399161427Z", "start_time": "2023-05-02T12:26:56.387512126Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -418,7 +436,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:26:54.214820993Z", "start_time": "2023-05-02T12:26:54.203960372Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -440,7 +459,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:27:05.534234034Z", "start_time": "2023-05-02T12:27:05.527369317Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -462,7 +482,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:27:06.296801083Z", "start_time": "2023-05-02T12:27:06.292091840Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -476,7 +497,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:27:46.379850935Z", "start_time": "2023-05-02T12:27:46.368793177Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -505,7 +527,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:27:51.093768675Z", "start_time": "2023-05-02T12:27:51.036828067Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -527,7 +550,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:27:54.352306250Z", "start_time": "2023-05-02T12:27:54.346310783Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -558,7 +582,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:27:56.752528818Z", "start_time": "2023-05-02T12:27:56.657579646Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -580,7 +605,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:27:58.048776998Z", "start_time": "2023-05-02T12:27:58.042439513Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -611,7 +637,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:32:21.218218856Z", "start_time": "2023-05-02T12:32:21.142651261Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -633,7 +660,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:32:27.206572755Z", "start_time": "2023-05-02T12:32:27.186853182Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -643,6 +671,60 @@ ")" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# invoice_model." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add Items to Invoices" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "invoices_item_models = invoice_model.get_item_model_qs()\n", + "\n", + "K = 6\n", + "\n", + "invoice_itemtxs = {\n", + " im.item_number: {\n", + " 'unit_cost': round(random() * 10, 2),\n", + " 'quantity': round(random() * 100, 2),\n", + " 'total_amount': None\n", + " } for im in choices(invoices_item_models, k=K)\n", + "}\n", + "\n", + "# Choose operation ITEMIZE_APPEND to append itemtxs...\n", + "invoice_itemtxs = invoice_model.migrate_itemtxs(itemtxs=invoice_itemtxs,\n", + " commit=True,\n", + " operation=InvoiceModel.ITEMIZE_REPLACE)\n", + "invoice_itemtxs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "invoice_model.amount_due" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -664,7 +746,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:37:18.049868700Z", "start_time": "2023-05-02T12:37:17.947993149Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -686,7 +769,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:37:26.105399746Z", "start_time": "2023-05-02T12:37:26.047062814Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -696,6 +780,52 @@ ")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add Items to Bills" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "bill_item_models = bill_model.get_item_model_qs()\n", + "\n", + "K = 6\n", + "\n", + "bill_itemtxs = {\n", + " im.item_number: {\n", + " 'unit_cost': round(random() * 10, 2),\n", + " 'quantity': round(random() * 100, 2),\n", + " 'total_amount': None\n", + " } for im in choices(bill_item_models, k=K)\n", + "}\n", + "\n", + "# Choose operation ITEMIZE_APPEND to append itemtxs...\n", + "bill_itemtxs = bill_model.migrate_itemtxs(itemtxs=bill_itemtxs,\n", + " commit=True,\n", + " operation=BillModel.ITEMIZE_REPLACE)\n", + "\n", + "bill_itemtxs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "bill_model.amount_due" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -717,7 +847,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:43:41.688757495Z", "start_time": "2023-05-02T12:43:41.651313834Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -739,11 +870,58 @@ "ExecuteTime": { "end_time": "2023-05-02T12:44:42.654178152Z", "start_time": "2023-05-02T12:44:42.646667501Z" - } + }, + "tags": [] }, "outputs": [], "source": [ - "purchase_order = entity_model.create_purchase_order()" + "po_model = entity_model.create_purchase_order()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add Items to Purchase Orders" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "po_item_models = po_model.get_item_model_qs()\n", + "\n", + "K = 6\n", + "\n", + "po_itemtxs = {\n", + " im.item_number: {\n", + " 'unit_cost': round(random() * 10, 2),\n", + " 'quantity': round(random() * 100, 2),\n", + " 'total_amount': None\n", + " } for im in choices(po_item_models, k=K)\n", + "}\n", + "\n", + "# Choose operation ITEMIZE_APPEND to append itemtxs...\n", + "po_itemtxs = po_model.migrate_itemtxs(itemtxs=po_itemtxs,\n", + " commit=True,\n", + " operation=EstimateModel.ITEMIZE_REPLACE)\n", + "\n", + "po_itemtxs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "po_model.po_amount" ] }, { @@ -767,7 +945,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:45:59.156196405Z", "start_time": "2023-05-02T12:45:58.942829638Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -789,7 +968,8 @@ "ExecuteTime": { "end_time": "2023-05-02T12:49:33.186131292Z", "start_time": "2023-05-02T12:49:33.125270860Z" - } + }, + "tags": [] }, "outputs": [], "source": [ @@ -800,6 +980,86 @@ ")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add Items to Estimates" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "estimate_item_models = estimate_model.get_item_model_qs()\n", + "\n", + "K = 6\n", + "\n", + "estimate_itemtxs = {\n", + " im.item_number: {\n", + " 'unit_cost': round(random() * 10, 2),\n", + " 'unit_revenue': round(random() * 20, 2),\n", + " 'quantity': round(random() * 100, 2),\n", + " 'total_amount': None\n", + " } for im in choices(estimate_item_models, k=K)\n", + "}\n", + "\n", + "# Choose operation ITEMIZE_APPEND to append itemtxs...\n", + "estimate_itemtxs = estimate_model.migrate_itemtxs(itemtxs=estimate_itemtxs,\n", + " commit=True,\n", + " operation=EstimateModel.ITEMIZE_REPLACE)\n", + "\n", + "estimate_itemtxs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "estimate_model.get_cost_estimate()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "estimate_model.get_revenue_estimate()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "estimate_model.get_profit_estimate()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "estimate_model.get_gross_margin_estimate(as_percent=True)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1289,7 +1549,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ "io_digest['tx_digest']['cash_flow_statement']" @@ -1316,5 +1578,5 @@ } }, "nbformat": 4, - "nbformat_minor": 1 + "nbformat_minor": 4 } diff --git a/pyproject.toml b/pyproject.toml index d44fb806..8a24886e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "django-ledger" -version = "0.5.3.1" +version = "0.5.3.2" readme = "README.md" requires-python = ">=3.7" description = "Bookkeeping & Financial analysis backend for Django. Balance Sheet, Income Statements, Chart of Accounts, Entities"