diff --git a/legal-api/poetry.lock b/legal-api/poetry.lock index 5099ccd0f0..1fb69579a2 100644 --- a/legal-api/poetry.lock +++ b/legal-api/poetry.lock @@ -875,8 +875,6 @@ files = [ {file = "greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d"}, {file = "greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5"}, {file = "greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f"}, - {file = "greenlet-3.2.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f47617f698838ba98f4ff4189aef02e7343952df3a615f847bb575c3feb177a7"}, - {file = "greenlet-3.2.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af41be48a4f60429d5cad9d22175217805098a9ef7c40bfef44f7669fb9d74d8"}, {file = "greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c"}, {file = "greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2"}, {file = "greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246"}, @@ -886,8 +884,6 @@ files = [ {file = "greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8"}, {file = "greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52"}, {file = "greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa"}, - {file = "greenlet-3.2.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c9c6de1940a7d828635fbd254d69db79e54619f165ee7ce32fda763a9cb6a58c"}, - {file = "greenlet-3.2.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03c5136e7be905045160b1b9fdca93dd6727b180feeafda6818e6496434ed8c5"}, {file = "greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9"}, {file = "greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd"}, {file = "greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb"}, @@ -897,8 +893,6 @@ files = [ {file = "greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0"}, {file = "greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0"}, {file = "greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f"}, - {file = "greenlet-3.2.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee7a6ec486883397d70eec05059353b8e83eca9168b9f3f9a361971e77e0bcd0"}, - {file = "greenlet-3.2.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:326d234cbf337c9c3def0676412eb7040a35a768efc92504b947b3e9cfc7543d"}, {file = "greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02"}, {file = "greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31"}, {file = "greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945"}, @@ -908,8 +902,6 @@ files = [ {file = "greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671"}, {file = "greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b"}, {file = "greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae"}, - {file = "greenlet-3.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e343822feb58ac4d0a1211bd9399de2b3a04963ddeec21530fc426cc121f19b"}, - {file = "greenlet-3.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca7f6f1f2649b89ce02f6f229d7c19f680a6238af656f61e0115b24857917929"}, {file = "greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b"}, {file = "greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0"}, {file = "greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f"}, @@ -917,8 +909,6 @@ files = [ {file = "greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1"}, {file = "greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735"}, {file = "greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337"}, - {file = "greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269"}, - {file = "greenlet-3.2.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:015d48959d4add5d6c9f6c5210ee3803a830dce46356e3bc326d6776bde54681"}, {file = "greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01"}, {file = "greenlet-3.2.4-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:b6a7c19cf0d2742d0809a4c05975db036fdff50cd294a93632d6a310bf9ac02c"}, {file = "greenlet-3.2.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:27890167f55d2387576d1f41d9487ef171849ea0359ce1510ca6e06c8bece11d"}, @@ -928,8 +918,6 @@ files = [ {file = "greenlet-3.2.4-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9913f1a30e4526f432991f89ae263459b1c64d1608c0d22a5c79c287b3c70df"}, {file = "greenlet-3.2.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b90654e092f928f110e0007f572007c9727b5265f7632c2fa7415b4689351594"}, {file = "greenlet-3.2.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81701fd84f26330f0d5f4944d4e92e61afe6319dcd9775e39396e39d7c3e5f98"}, - {file = "greenlet-3.2.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:28a3c6b7cd72a96f61b0e4b2a36f681025b60ae4779cc73c1535eb5f29560b10"}, - {file = "greenlet-3.2.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:52206cd642670b0b320a1fd1cbfd95bca0e043179c1d8a045f2c6109dfe973be"}, {file = "greenlet-3.2.4-cp39-cp39-win32.whl", hash = "sha256:65458b409c1ed459ea899e939f0e1cdb14f58dbc803f2f93c5eab5694d32671b"}, {file = "greenlet-3.2.4-cp39-cp39-win_amd64.whl", hash = "sha256:d2e685ade4dafd447ede19c31277a224a239a0a1a4eca4e6390efedf20260cfb"}, {file = "greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d"}, @@ -1387,13 +1375,9 @@ files = [ {file = "lxml-5.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:edcfa83e03370032a489430215c1e7783128808fd3e2e0a3225deee278585196"}, {file = "lxml-5.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28bf95177400066596cdbcfc933312493799382879da504633d16cf60bba735b"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a745cc98d504d5bd2c19b10c79c61c7c3df9222629f1b6210c0368177589fb8"}, - {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b590b39ef90c6b22ec0be925b211298e810b4856909c8ca60d27ffbca6c12e6"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b336b0416828022bfd5a2e3083e7f5ba54b96242159f83c7e3eebaec752f1716"}, - {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:c2faf60c583af0d135e853c86ac2735ce178f0e338a3c7f9ae8f622fd2eb788c"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:4bc6cb140a7a0ad1f7bc37e018d0ed690b7b6520ade518285dc3171f7a117905"}, - {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7ff762670cada8e05b32bf1e4dc50b140790909caa8303cfddc4d702b71ea184"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:57f0a0bbc9868e10ebe874e9f129d2917750adf008fe7b9c1598c0fbbfdde6a6"}, - {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:a6d2092797b388342c1bc932077ad232f914351932353e2e8706851c870bca1f"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:60499fe961b21264e17a471ec296dcbf4365fbea611bf9e303ab69db7159ce61"}, {file = "lxml-5.2.2-cp37-cp37m-win32.whl", hash = "sha256:d9b342c76003c6b9336a80efcc766748a333573abf9350f4094ee46b006ec18f"}, {file = "lxml-5.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b16db2770517b8799c79aa80f4053cd6f8b716f21f8aca962725a9565ce3ee40"}, @@ -1648,8 +1632,6 @@ groups = ["main"] files = [ {file = "pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860"}, {file = "pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad"}, - {file = "pillow-11.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0"}, - {file = "pillow-11.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b"}, {file = "pillow-11.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50"}, {file = "pillow-11.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae"}, {file = "pillow-11.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9"}, @@ -1659,8 +1641,6 @@ files = [ {file = "pillow-11.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f"}, {file = "pillow-11.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722"}, {file = "pillow-11.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288"}, - {file = "pillow-11.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d"}, - {file = "pillow-11.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494"}, {file = "pillow-11.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58"}, {file = "pillow-11.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f"}, {file = "pillow-11.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e"}, @@ -1670,8 +1650,6 @@ files = [ {file = "pillow-11.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd"}, {file = "pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4"}, {file = "pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69"}, - {file = "pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d"}, - {file = "pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6"}, {file = "pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7"}, {file = "pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024"}, {file = "pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809"}, @@ -1684,8 +1662,6 @@ files = [ {file = "pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f"}, {file = "pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c"}, {file = "pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd"}, - {file = "pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e"}, - {file = "pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1"}, {file = "pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805"}, {file = "pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8"}, {file = "pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2"}, @@ -1695,8 +1671,6 @@ files = [ {file = "pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580"}, {file = "pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e"}, {file = "pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d"}, - {file = "pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced"}, - {file = "pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c"}, {file = "pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8"}, {file = "pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59"}, {file = "pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe"}, @@ -1706,8 +1680,6 @@ files = [ {file = "pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e"}, {file = "pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12"}, {file = "pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a"}, - {file = "pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632"}, - {file = "pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673"}, {file = "pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027"}, {file = "pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77"}, {file = "pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874"}, @@ -1717,8 +1689,6 @@ files = [ {file = "pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6"}, {file = "pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae"}, {file = "pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653"}, - {file = "pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6"}, - {file = "pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36"}, {file = "pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b"}, {file = "pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477"}, {file = "pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50"}, @@ -1728,8 +1698,6 @@ files = [ {file = "pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa"}, {file = "pillow-11.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:48d254f8a4c776de343051023eb61ffe818299eeac478da55227d96e241de53f"}, {file = "pillow-11.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7aee118e30a4cf54fdd873bd3a29de51e29105ab11f9aad8c32123f58c8f8081"}, - {file = "pillow-11.3.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:23cff760a9049c502721bdb743a7cb3e03365fafcdfc2ef9784610714166e5a4"}, - {file = "pillow-11.3.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6359a3bc43f57d5b375d1ad54a0074318a0844d11b76abccf478c37c986d3cfc"}, {file = "pillow-11.3.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:092c80c76635f5ecb10f3f83d76716165c96f5229addbd1ec2bdbbda7d496e06"}, {file = "pillow-11.3.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cadc9e0ea0a2431124cde7e1697106471fc4c1da01530e679b2391c37d3fbb3a"}, {file = "pillow-11.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6a418691000f2a418c9135a7cf0d797c1bb7d9a485e61fe8e7722845b95ef978"}, @@ -1739,15 +1707,11 @@ files = [ {file = "pillow-11.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:6abdbfd3aea42be05702a8dd98832329c167ee84400a1d1f61ab11437f1717eb"}, {file = "pillow-11.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967"}, {file = "pillow-11.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe"}, - {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c"}, - {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25"}, {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27"}, {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a"}, {file = "pillow-11.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f"}, {file = "pillow-11.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6"}, {file = "pillow-11.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438"}, - {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3"}, - {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c"}, {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361"}, {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7"}, {file = "pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8"}, @@ -2201,7 +2165,7 @@ typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.13\""} [[package]] name = "registry_schemas" -version = "2.18.58" +version = "2.18.59" description = "A short description of the project" optional = false python-versions = ">=3.6" @@ -2219,8 +2183,8 @@ strict-rfc3339 = "*" [package.source] type = "git" url = "https://github.com/bcgov/business-schemas.git" -reference = "2.18.58" -resolved_reference = "0a9c92e66a0a8c50d470c830ff73e5c38f020d3a" +reference = "2.18.60" +resolved_reference = "4e355909b8d16237421bf7eb391acd23e037d437" [[package]] name = "reportlab" @@ -3122,4 +3086,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.9.22,<3.10" -content-hash = "65b17fe0208d8938cb54d85d0a2c41d9ac0659a591778730e576e812aff50c49" +content-hash = "c256ff2667772a27ed6a47b64355b104f197a99bafe2b441679263fba0674bf4" diff --git a/legal-api/pyproject.toml b/legal-api/pyproject.toml index 9e25b629d4..0894515d07 100644 --- a/legal-api/pyproject.toml +++ b/legal-api/pyproject.toml @@ -52,7 +52,7 @@ dependencies = [ "blinker (==1.4)", "pyjwt (==2.8.0)", - "registry_schemas @ git+https://github.com/bcgov/business-schemas.git@2.18.58#egg=registry_schemas", + "registry_schemas @ git+https://github.com/bcgov/business-schemas.git@2.18.60#egg=registry_schemas", "sql-versioning @ git+https://github.com/bcgov/lear.git@main#subdirectory=python/common/sql-versioning", "gcp-queue @ git+https://github.com/bcgov/sbc-connect-common.git@main#subdirectory=python/gcp-queue", "structured-logging @ git+https://github.com/bcgov/sbc-connect-common.git@main#subdirectory=python/structured-logging" diff --git a/legal-api/src/legal_api/services/filings/validations/common_validations.py b/legal-api/src/legal_api/services/filings/validations/common_validations.py index d3cfcc6760..3a2f0db9d2 100644 --- a/legal-api/src/legal_api/services/filings/validations/common_validations.py +++ b/legal-api/src/legal_api/services/filings/validations/common_validations.py @@ -23,6 +23,7 @@ from flask import current_app, g, request from flask_babel import _ +from legal_api.core.filing import Filing from legal_api.errors import Error from legal_api.models import Address, Business, PartyRole from legal_api.models.configuration import EMAIL_PATTERN @@ -50,16 +51,11 @@ ) -def has_at_least_one_share_class(filing_json, filing_type) -> Optional[str]: # pylint: disable=too-many-branches - """Ensure that share structure contain at least 1 class by the end of the alteration or IA Correction filing.""" - if filing_type in filing_json["filing"] and "shareStructure" in filing_json["filing"][filing_type]: - share_classes = filing_json["filing"][filing_type] \ - .get("shareStructure", {}).get("shareClasses", []) - - if len(share_classes) == 0: - return "A company must have a minimum of one share class." - - return None +# Share structure constants +EXCLUDED_WORDS_FOR_CLASS = ["share", "shares", "value"] +EXCLUDED_WORDS_FOR_SERIES = ["share", "shares"] +SHARE_NAME_SUFFIX = " Shares" +MAX_SHARE_DIGITS = 16 def validate_resolution_date_in_share_structure(filing_json, filing_type) -> Optional[dict]: @@ -93,6 +89,14 @@ def validate_share_structure(incorporation_json, filing_type, legal_type) -> Err msg = [] memoize_names = [] + # For incorporation applications, at least one share class is required, for Alteration can not include if not changing + if filing_type == Filing.FilingTypes.INCORPORATIONAPPLICATION.value and len(share_classes) == 0: + msg.append({ + "error": "A company must have at least one Class of Shares.", + "path": f"/filing/{filing_type}/shareStructure/shareClasses" + }) + return msg + for index, item in enumerate(share_classes): shares_msg = validate_shares(item, memoize_names, filing_type, index, legal_type) if shares_msg: @@ -104,7 +108,15 @@ def validate_share_structure(incorporation_json, filing_type, legal_type) -> Err return None -def validate_series(item, memoize_names, filing_type, index) -> Error: +def _series_name_has_reserved_words(series_name: str) -> bool: + """Check if the series name contains reserved words (excluding the required suffix).""" + suffix_len = len(SHARE_NAME_SUFFIX) + name_without_suffix = series_name[:-suffix_len] if series_name.endswith(SHARE_NAME_SUFFIX) else series_name + series_name_words = name_without_suffix.lower().split() + return any(word in EXCLUDED_WORDS_FOR_SERIES for word in series_name_words) + + +def validate_series(item, memoize_names, filing_type, index) -> Error: # noqa: PLR0912 """Validate shareStructure includes a wellformed series.""" msg = [] for series_index, series in enumerate(item.get("series", [])): @@ -124,6 +136,18 @@ def validate_series(item, memoize_names, filing_type, index) -> Error: "error": "Share series name cannot start or end with whitespace.", "path": f"{err_path}/name/" }) + + elif not series_name.endswith(SHARE_NAME_SUFFIX): + msg.append({ + "error": f"Share series name '{series_name}' must end with '{SHARE_NAME_SUFFIX}'.", + "path": f"{err_path}/name/" + }) + + elif _series_name_has_reserved_words(series_name): + msg.append({ + "error": "Share series name cannot contain the words 'share' or 'shares'.", + "path": f"{err_path}/name/" + }) elif series_name in memoize_names: msg.append({"error": f"Share series {series_name} name already used in a share class or series.", @@ -131,22 +155,54 @@ def validate_series(item, memoize_names, filing_type, index) -> Error: else: memoize_names.append(series_name) + if series["hasMaximumShares"]: - if not series.get("maxNumberOfShares", None): + max_shares = series.get("maxNumberOfShares", None) + if max_shares is None: + msg.append({ + "error": f"Share series {series['name']} must provide value for maximum number of shares", + "path": f"{err_path}/maxNumberOfShares" + }) + elif not (isinstance(max_shares, int) and not isinstance(max_shares, bool)): + msg.append({ + "error": "Must be a whole number", + "path": f"{err_path}/maxNumberOfShares" + }) + elif max_shares <= 0: + msg.append({ + "error": "Number must be greater than 0", + "path": f"{err_path}/maxNumberOfShares" + }) + elif len(str(abs(max_shares))) >= MAX_SHARE_DIGITS: msg.append({ - "error": "Share series {} must provide value for maximum number of shares".format(series["name"]), + "error": "Number must be less than 16 digits", "path": f"{err_path}/maxNumberOfShares" }) - elif item["hasMaximumShares"] and item.get("maxNumberOfShares", None) and \ - int(series["maxNumberOfShares"]) > int(item["maxNumberOfShares"]): + # Check series shares do not exceed class shares + elif ( + item["hasMaximumShares"] + and item.get("maxNumberOfShares", None) + and isinstance(item["maxNumberOfShares"], int) + and not isinstance(item["maxNumberOfShares"], bool) + and max_shares > item["maxNumberOfShares"] + ): msg.append({ - "error": "Series {} share quantity must be less than or equal to that of its class {}".format(series["name"], item["name"]), + "error": f"Series {series['name']} share quantity must be less than or equal to that of its class {item['name']}", "path": f"{err_path}/maxNumberOfShares" }) return msg -def validate_shares(item, memoize_names, filing_type, index, legal_type) -> Error: +def _class_name_has_reserved_words(share_name: str) -> bool: + # Validate share class name does not contain reserved words (excluding the required suffix) + # Remove the suffix before checking for reserved words + suffix_len = len(SHARE_NAME_SUFFIX) + name_without_suffix = share_name[:-suffix_len] if share_name.endswith(SHARE_NAME_SUFFIX) else share_name + name_words = name_without_suffix.lower().split() + return any(word in EXCLUDED_WORDS_FOR_CLASS for word in name_words) + + +def validate_shares(item, memoize_names, filing_type, index, legal_type) -> Error: # noqa: PLR0912 """Validate a wellformed share structure.""" msg = [] @@ -166,6 +222,20 @@ def validate_shares(item, memoize_names, filing_type, index, legal_type) -> Erro "error": "Share class name cannot start or end with whitespace.", "path": err_path }) + + elif not share_name.endswith(SHARE_NAME_SUFFIX): + err_path = f"/filing/{filing_type}/shareClasses/{index}/name/" + msg.append({ + "error": f"Share class name '{share_name}' must end with '{SHARE_NAME_SUFFIX}'.", + "path": err_path + }) + + elif _class_name_has_reserved_words(share_name): + err_path = f"/filing/{filing_type}/shareClasses/{index}/name/" + msg.append({ + "error": "Share class name cannot contain the words 'share', 'shares', or 'value'.", + "path": err_path + }) elif share_name in memoize_names: err_path = f"/filing/{filing_type}/shareClasses/{index}/name/" @@ -174,10 +244,29 @@ def validate_shares(item, memoize_names, filing_type, index, legal_type) -> Erro else: memoize_names.append(share_name) - if item["hasMaximumShares"] and not item.get("maxNumberOfShares", None): + if item["hasMaximumShares"]: + max_shares = item.get("maxNumberOfShares", None) err_path = f"/filing/{filing_type}/shareClasses/{index}/maxNumberOfShares/" - msg.append({"error": "Share class {} must provide value for maximum number of shares".format(item["name"]), - "path": err_path}) + if max_shares is None: + msg.append({ + "error": f"Share class {item['name']} must provide value for maximum number of shares", + "path": err_path + }) + elif not (isinstance(max_shares, int) and not isinstance(max_shares, bool)): + msg.append({ + "error": "Must be a whole number", + "path": err_path + }) + elif max_shares <= 0: + msg.append({ + "error": "Number must be greater than 0", + "path": err_path + }) + elif len(str(abs(max_shares))) >= MAX_SHARE_DIGITS: + msg.append({ + "error": "Number must be less than 16 digits", + "path": err_path + }) if item["hasParValue"]: if not item.get("parValue", None): err_path = f"/filing/{filing_type}/shareClasses/{index}/parValue/" diff --git a/legal-api/tests/unit/models/__init__.py b/legal-api/tests/unit/models/__init__.py index 0594f903b2..a393ea2012 100644 --- a/legal-api/tests/unit/models/__init__.py +++ b/legal-api/tests/unit/models/__init__.py @@ -362,7 +362,7 @@ def factory_share_class(business_identifier: str): """Create a share class.""" business = factory_business(business_identifier) share_class = ShareClass( - name='Share Class 1', + name='Class 1 Shares', priority=1, max_share_flag=True, max_shares=1000, @@ -373,7 +373,7 @@ def factory_share_class(business_identifier: str): business_id=business.id ) share_series_1 = ShareSeries( - name='Share Series 1', + name='Series 1 Shares', priority=1, max_share_flag=True, max_shares=500, diff --git a/legal-api/tests/unit/models/test_share_class.py b/legal-api/tests/unit/models/test_share_class.py index 4be514b721..6bb5d64f4f 100644 --- a/legal-api/tests/unit/models/test_share_class.py +++ b/legal-api/tests/unit/models/test_share_class.py @@ -31,7 +31,7 @@ def test_valid_share_class_save(session): identifier = 'CP1234567' business = factory_business(identifier) share_class = ShareClass( - name='Share Class 1', + name='Class 1 Shares', priority=1, max_share_flag=True, max_shares=1000, @@ -50,7 +50,7 @@ def test_share_class_json(session): identifier = 'CP1234567' business = factory_business(identifier) share_class = ShareClass( - name='Share Class 1', + name='Class 1 Shares', priority=1, max_share_flag=True, max_shares=1000, @@ -81,7 +81,7 @@ def test_invalid_share_quantity(session): identifier = 'CP1234567' business = factory_business(identifier) share_class = ShareClass( - name='Share Class 1', + name='Class 1 Shares', priority=1, max_share_flag=True, max_shares=None, @@ -105,7 +105,7 @@ def test_invalid_par_value(session): identifier = 'CP1234567' business = factory_business(identifier) share_class = ShareClass( - name='Share Class 1', + name='Class 1 Shares', priority=1, max_share_flag=True, max_shares=1000, @@ -129,7 +129,7 @@ def test_share_class_currency(session): identifier = 'CP1234567' business = factory_business(identifier) share_class = ShareClass( - name='Share Class 1', + name='Class 1 Shares', priority=1, max_share_flag=True, max_shares=1000, @@ -153,7 +153,7 @@ def test_find_by_share_class_id(session): identifier = 'CP1234567' business = factory_business(identifier) share_class = ShareClass( - name='Share Class 1', + name='Class 1 Shares', priority=1, max_share_flag=True, max_shares=1000, diff --git a/legal-api/tests/unit/models/test_share_series.py b/legal-api/tests/unit/models/test_share_series.py index 2d1228b991..9239c11078 100644 --- a/legal-api/tests/unit/models/test_share_series.py +++ b/legal-api/tests/unit/models/test_share_series.py @@ -31,7 +31,7 @@ def test_valid_share_series_save(session): identifier = 'CP1234567' business = factory_business(identifier) share_class = ShareClass( - name='Share Class 1', + name='Class 1 Shares', priority=1, max_share_flag=True, max_shares=1000, @@ -42,14 +42,14 @@ def test_valid_share_series_save(session): business_id=business.id ) share_series_1 = ShareSeries( - name='Share Series 1', + name='Series 1 Shares', priority=1, max_share_flag=True, max_shares=500, special_rights_flag=False ) share_series_2 = ShareSeries( - name='Share Series 2', + name='Series 2 Shares', priority=2, max_share_flag=True, max_shares=1000, @@ -67,7 +67,7 @@ def test_share_series_json(session): identifier = 'CP1234567' business = factory_business(identifier) share_class = ShareClass( - name='Share Class 1', + name='Class 1 Shares', priority=1, max_share_flag=True, max_shares=1000, @@ -78,7 +78,7 @@ def test_share_series_json(session): business_id=business.id ) share_series_1 = ShareSeries( - name='Share Series 1', + name='Series 1 Shares', priority=1, max_share_flag=True, max_shares=500, @@ -116,7 +116,7 @@ def test_invalid_share_quantity(session): identifier = 'CP1234567' business = factory_business(identifier) share_class = ShareClass( - name='Share Class 1', + name='Class 1 Shares', priority=1, max_share_flag=True, max_shares=1000, @@ -127,7 +127,7 @@ def test_invalid_share_quantity(session): business_id=business.id ) share_series_1 = ShareSeries( - name='Share Series 1', + name='Series 1 Shares', priority=1, max_share_flag=True, max_shares=None, @@ -150,7 +150,7 @@ def test_validate_share_quantity_not_exceed_class_value(session): identifier = 'CP1234567' business = factory_business(identifier) share_class = ShareClass( - name='Share Class 1', + name='Class 1 Shares', priority=1, max_share_flag=True, max_shares=1000, @@ -161,7 +161,7 @@ def test_validate_share_quantity_not_exceed_class_value(session): business_id=business.id ) share_series_1 = ShareSeries( - name='Share Series 1', + name='Series 1 Shares', priority=1, max_share_flag=True, max_shares=1500, @@ -184,7 +184,7 @@ def test_share_quantity_when_no_max_share_for_parent(session): identifier = 'CP1234567' business = factory_business(identifier) share_class = ShareClass( - name='Share Class 1', + name='Class 1 Shares', priority=1, max_share_flag=False, max_shares=None, @@ -195,7 +195,7 @@ def test_share_quantity_when_no_max_share_for_parent(session): business_id=business.id ) share_series_1 = ShareSeries( - name='Share Series 1', + name='Series 1 Shares', priority=1, max_share_flag=True, max_shares=1500, diff --git a/legal-api/tests/unit/resources/v2/test_business_filings/test_filings.py b/legal-api/tests/unit/resources/v2/test_business_filings/test_filings.py index f44a3532d3..3fe89762d8 100644 --- a/legal-api/tests/unit/resources/v2/test_business_filings/test_filings.py +++ b/legal-api/tests/unit/resources/v2/test_business_filings/test_filings.py @@ -605,6 +605,32 @@ def test_post_validate_ar_valid_routing_slip(session, client, jwt): assert not rv.json.get('errors') +@pytest.mark.parametrize('only_validate', [True, False]) +def test_post_cod_with_empty_directors_array(session, client, jwt, only_validate): + """Assert that a change of directors filing with empty directors array fails validation.""" + identifier = 'CP7654321' + factory_business(identifier) + + cod = copy.deepcopy(FILING_HEADER) + cod['filing']['header']['name'] = 'changeOfDirectors' + cod['filing']['changeOfDirectors'] = copy.deepcopy(CHANGE_OF_DIRECTORS) + # Set empty directors array - this should fail validation due to minItems: 1 in schema + cod['filing']['changeOfDirectors']['directors'] = [] + + url = f'/api/v2/businesses/{identifier}/filings' + if only_validate: + url += '?only_validate=true' + + rv = client.post(url, json=cod, headers=create_header(jwt, [STAFF_ROLE], identifier)) + + # Should fail validation with BAD_REQUEST or UNPROCESSABLE_ENTITY + assert rv.status_code in (HTTPStatus.BAD_REQUEST, HTTPStatus.UNPROCESSABLE_ENTITY) + assert rv.json.get('errors') + # The error should mention directors array being empty or having minimum items requirement + errors_str = str(rv.json['errors']) + assert 'directors' in errors_str.lower() or 'minItems' in errors_str or 'minimum' in errors_str.lower() + + @integration_payment def test_post_valid_ar(session, client, jwt): """Assert that a filing can be completed up to payment.""" diff --git a/legal-api/tests/unit/services/filings/validations/test_amalgamation_application.py b/legal-api/tests/unit/services/filings/validations/test_amalgamation_application.py index c64a9b32d0..34030d4d1f 100644 --- a/legal-api/tests/unit/services/filings/validations/test_amalgamation_application.py +++ b/legal-api/tests/unit/services/filings/validations/test_amalgamation_application.py @@ -382,236 +382,236 @@ def test_validate_amalgamation_office(session, mocker, test_name, legal_type, de 'class_name_2,series_name_2,' 'expected_code, expected_msg', [ - ('SUCCESS', 'BEN', 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'BEN', 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'BEN', 'Share Class 1', False, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'BEN', 'Class 1 Shares', False, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'BEN', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, + ('SUCCESS', 'BEN', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, None, None, None, None), - ('SUCCESS-CLASS2', 'BEN', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', None, None, None), + ('SUCCESS-CLASS2', 'BEN', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', None, None, None), ('FAIL-CLASS2', 'BEN', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 1', None, + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 1 Shares', None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 name already used in a share class or series.', + 'error': 'Share class Class 1 Shares name already used in a share class or series.', 'path': '/filing/amalgamationApplication/shareClasses/1/name/' }]), ('FAIL-SERIES2', 'BEN', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', 'Share Series 1', + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', 'Series 1 Shares', HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 name already used in a share class or series.', + 'error': 'Share series Series 1 Shares name already used in a share class or series.', 'path': '/filing/amalgamationApplication/shareClasses/0/series/1' }]), ('FAIL_INVALID_CLASS_MAX_SHARES', 'BEN', - 'Share Class 1', True, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must provide value for maximum number of shares', + 'error': 'Share class Class 1 Shares must provide value for maximum number of shares', 'path': '/filing/amalgamationApplication/shareClasses/0/maxNumberOfShares/' }]), ('FAIL_INVALID_CURRENCY', 'BEN', - 'Share Class 1', True, 5000, True, 0.875, None, 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, 0.875, None, 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify currency', + 'error': 'Share class Class 1 Shares must specify currency', 'path': '/filing/amalgamationApplication/shareClasses/0/currency/' }]), ('FAIL_INVALID_PAR_VALUE', 'BEN', - 'Share Class 1', True, 5000, True, None, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, None, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify par value', + 'error': 'Share class Class 1 Shares must specify par value', 'path': '/filing/amalgamationApplication/shareClasses/0/parValue/' }]), ('FAIL_INVALID_SERIES_MAX_SHARES', 'BEN', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, None, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, None, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 must provide value for maximum number of shares', + 'error': 'Share series Series 1 Shares must provide value for maximum number of shares', 'path': '/filing/amalgamationApplication/shareClasses/0/series/0/maxNumberOfShares' }]), ('FAIL_SERIES_SHARES_EXCEEDS_CLASS_SHARES', 'BEN', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 10000, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 10000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': - 'Series Share Series 1 share quantity must be less than or equal to that of its class Share Class 1', + 'Series Series 1 Shares share quantity must be less than or equal to that of its class Class 1 Shares', 'path': '/filing/amalgamationApplication/shareClasses/0/series/0/maxNumberOfShares' }]), - ('SUCCESS', 'BC', 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'BC', 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'BC', 'Share Class 1', False, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'BC', 'Class 1 Shares', False, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'BC', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, + ('SUCCESS', 'BC', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, None, None, None, None), - ('SUCCESS-CLASS2', 'BC', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', None, None, None), + ('SUCCESS-CLASS2', 'BC', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', None, None, None), ('FAIL-CLASS2', 'BC', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 1', None, + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 1 Shares', None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 name already used in a share class or series.', + 'error': 'Share class Class 1 Shares name already used in a share class or series.', 'path': '/filing/amalgamationApplication/shareClasses/1/name/' }]), ('FAIL-SERIES2', 'BC', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', 'Share Series 1', + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', 'Series 1 Shares', HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 name already used in a share class or series.', + 'error': 'Share series Series 1 Shares name already used in a share class or series.', 'path': '/filing/amalgamationApplication/shareClasses/0/series/1' }]), ('FAIL_INVALID_CLASS_MAX_SHARES', 'BC', - 'Share Class 1', True, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must provide value for maximum number of shares', + 'error': 'Share class Class 1 Shares must provide value for maximum number of shares', 'path': '/filing/amalgamationApplication/shareClasses/0/maxNumberOfShares/' }]), ('FAIL_INVALID_CURRENCY', 'BC', - 'Share Class 1', True, 5000, True, 0.875, None, 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, 0.875, None, 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify currency', + 'error': 'Share class Class 1 Shares must specify currency', 'path': '/filing/amalgamationApplication/shareClasses/0/currency/' }]), ('FAIL_INVALID_PAR_VALUE', 'BC', - 'Share Class 1', True, 5000, True, None, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, None, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify par value', + 'error': 'Share class Class 1 Shares must specify par value', 'path': '/filing/amalgamationApplication/shareClasses/0/parValue/' }]), ('FAIL_INVALID_SERIES_MAX_SHARES', 'BC', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, None, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, None, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 must provide value for maximum number of shares', + 'error': 'Share series Series 1 Shares must provide value for maximum number of shares', 'path': '/filing/amalgamationApplication/shareClasses/0/series/0/maxNumberOfShares' }]), ('FAIL_SERIES_SHARES_EXCEEDS_CLASS_SHARES', 'BC', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 10000, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 10000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': - 'Series Share Series 1 share quantity must be less than or equal to that of its class Share Class 1', + 'Series Series 1 Shares share quantity must be less than or equal to that of its class Class 1 Shares', 'path': '/filing/amalgamationApplication/shareClasses/0/series/0/maxNumberOfShares' }]), - ('SUCCESS', 'ULC', 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'ULC', 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'ULC', 'Share Class 1', False, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'ULC', 'Class 1 Shares', False, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'ULC', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, + ('SUCCESS', 'ULC', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, None, None, None, None), - ('SUCCESS-CLASS2', 'ULC', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', None, None, None), + ('SUCCESS-CLASS2', 'ULC', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', None, None, None), ('FAIL-CLASS2', 'ULC', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 1', None, + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 1 Shares', None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 name already used in a share class or series.', + 'error': 'Share class Class 1 Shares name already used in a share class or series.', 'path': '/filing/amalgamationApplication/shareClasses/1/name/' }]), ('FAIL-SERIES2', 'ULC', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', 'Share Series 1', + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', 'Series 1 Shares', HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 name already used in a share class or series.', + 'error': 'Share series Series 1 Shares name already used in a share class or series.', 'path': '/filing/amalgamationApplication/shareClasses/0/series/1' }]), ('FAIL_INVALID_CLASS_MAX_SHARES', 'ULC', - 'Share Class 1', True, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must provide value for maximum number of shares', + 'error': 'Share class Class 1 Shares must provide value for maximum number of shares', 'path': '/filing/amalgamationApplication/shareClasses/0/maxNumberOfShares/' }]), ('FAIL_INVALID_CURRENCY', 'ULC', - 'Share Class 1', True, 5000, True, 0.875, None, 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, 0.875, None, 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify currency', + 'error': 'Share class Class 1 Shares must specify currency', 'path': '/filing/amalgamationApplication/shareClasses/0/currency/' }]), ('FAIL_INVALID_PAR_VALUE', 'ULC', - 'Share Class 1', True, 5000, True, None, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, None, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify par value', + 'error': 'Share class Class 1 Shares must specify par value', 'path': '/filing/amalgamationApplication/shareClasses/0/parValue/' }]), ('FAIL_INVALID_SERIES_MAX_SHARES', 'ULC', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, None, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, None, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 must provide value for maximum number of shares', + 'error': 'Share series Series 1 Shares must provide value for maximum number of shares', 'path': '/filing/amalgamationApplication/shareClasses/0/series/0/maxNumberOfShares' }]), ('FAIL_SERIES_SHARES_EXCEEDS_CLASS_SHARES', 'ULC', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 10000, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 10000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': - 'Series Share Series 1 share quantity must be less than or equal to that of its class Share Class 1', + 'Series Series 1 Shares share quantity must be less than or equal to that of its class Class 1 Shares', 'path': '/filing/amalgamationApplication/shareClasses/0/series/0/maxNumberOfShares' }]), - ('SUCCESS', 'CC', 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'CC', 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'CC', 'Share Class 1', False, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'CC', 'Class 1 Shares', False, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'CC', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, + ('SUCCESS', 'CC', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, None, None, None, None), - ('SUCCESS-CLASS2', 'CC', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', None, None, None), + ('SUCCESS-CLASS2', 'CC', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', None, None, None), ('FAIL-CLASS2', 'CC', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 1', None, + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 1 Shares', None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 name already used in a share class or series.', + 'error': 'Share class Class 1 Shares name already used in a share class or series.', 'path': '/filing/amalgamationApplication/shareClasses/1/name/' }]), ('FAIL-SERIES2', 'CC', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', 'Share Series 1', + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', 'Series 1 Shares', HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 name already used in a share class or series.', + 'error': 'Share series Series 1 Shares name already used in a share class or series.', 'path': '/filing/amalgamationApplication/shareClasses/0/series/1' }]), ('FAIL_INVALID_CLASS_MAX_SHARES', 'CC', - 'Share Class 1', True, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must provide value for maximum number of shares', + 'error': 'Share class Class 1 Shares must provide value for maximum number of shares', 'path': '/filing/amalgamationApplication/shareClasses/0/maxNumberOfShares/' }]), ('FAIL_INVALID_CURRENCY', 'CC', - 'Share Class 1', True, 5000, True, 0.875, None, 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, 0.875, None, 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify currency', + 'error': 'Share class Class 1 Shares must specify currency', 'path': '/filing/amalgamationApplication/shareClasses/0/currency/' }]), ('FAIL_INVALID_PAR_VALUE', 'CC', - 'Share Class 1', True, 5000, True, None, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, None, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify par value', + 'error': 'Share class Class 1 Shares must specify par value', 'path': '/filing/amalgamationApplication/shareClasses/0/parValue/' }]), ('FAIL_INVALID_SERIES_MAX_SHARES', 'CC', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, None, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, None, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 must provide value for maximum number of shares', + 'error': 'Share series Series 1 Shares must provide value for maximum number of shares', 'path': '/filing/amalgamationApplication/shareClasses/0/series/0/maxNumberOfShares' }]), ('FAIL_SERIES_SHARES_EXCEEDS_CLASS_SHARES', 'CC', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 10000, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 10000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': - 'Series Share Series 1 share quantity must be less than or equal to that of its class Share Class 1', + 'Series Series 1 Shares share quantity must be less than or equal to that of its class Class 1 Shares', 'path': '/filing/amalgamationApplication/shareClasses/0/series/0/maxNumberOfShares' }]) ]) diff --git a/legal-api/tests/unit/services/filings/validations/test_common_validations.py b/legal-api/tests/unit/services/filings/validations/test_common_validations.py index de56d73f01..31ed718198 100644 --- a/legal-api/tests/unit/services/filings/validations/test_common_validations.py +++ b/legal-api/tests/unit/services/filings/validations/test_common_validations.py @@ -45,6 +45,8 @@ from tests.unit.models import factory_business, factory_party_role from legal_api.services.filings.validations.common_validations import ( + EXCLUDED_WORDS_FOR_CLASS, + EXCLUDED_WORDS_FOR_SERIES, find_updated_keys_for_firms, is_officer_proprietor_replace_valid, validate_certify_name, @@ -55,6 +57,9 @@ validate_parties_addresses, validate_party_name, validate_party_role_firms, + validate_series, + validate_share_structure, + validate_shares, validate_staff_payment, ) @@ -810,3 +815,666 @@ def test_validate_court_order_with_flag_on(session, has_permission, expected_err assert len(result) == 1 assert expected_error_msg in result[0]['error'] assert result[0]['path'] == '/filing/alteration/courtOrder' + +@pytest.mark.parametrize('share_class_name, expected_valid', [ + # Valid names - end with " Shares" + ('Class A Shares', True), + ('Common Shares', True), + ('Preferred Shares', True), + ('Class B Non-Voting Shares', True), + ('Series 1 Preferred Shares', True), + ('Voting Shares', True), + ('Non-Voting Shares', True), + # Invalid names - don't end with " Shares" + ('Class A', False), + ('Common', False), + ('Preferred Stock', False), + ('SharesClass', False), + ('', False), # empty name handled separately +]) +def test_share_class_name_must_end_with_shares(session, share_class_name, expected_valid): + """Test that share class names must end with ' Shares'.""" + share_class = { + 'name': share_class_name, + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': False, + 'series': [] + } + memoize_names = [] + + result = validate_shares(share_class, memoize_names, 'incorporationApplication', 0, 'BEN') + + suffix_errors = [e for e in result if "must end with ' Shares'" in e.get('error', '')] + + if expected_valid: + assert len(suffix_errors) == 0 + else: + if share_class_name.strip(): # only check suffix error if name is not empty + assert len(suffix_errors) == 1 + assert f"Share class name '{share_class_name}' must end with ' Shares'." in suffix_errors[0]['error'] + + +@pytest.mark.parametrize('share_class_name, expected_valid', [ + # Valid names - no reserved words before " Shares" suffix + ('Class A Shares', True), + ('Common Shares', True), + ('Preferred Shares', True), + ('Class B Non-Voting Shares', True), + ('Voting Shares', True), + # Invalid names - contain reserved word "share" (case insensitive) + ('Share Class A Shares', False), + ('My Share Shares', False), + ('SHARE Type Shares', False), + # Invalid names - contain reserved word "shares" (case insensitive) + ('Shares Class Shares', False), + ('Multiple Shares Type Shares', False), + # Invalid names - contain reserved word "value" (case insensitive) + ('Value Class Shares', False), + ('Par Value Shares', False), + ('VALUE Type Shares', False), + ('No Par Value Shares', False), +]) +def test_share_class_name_reserved_words(session, share_class_name, expected_valid): + """Test that share class names cannot contain reserved words.""" + share_class = { + 'name': share_class_name, + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': False, + 'series': [] + } + memoize_names = [] + + result = validate_shares(share_class, memoize_names, 'incorporationApplication', 0, 'BEN') + + reserved_word_errors = [e for e in result if "cannot contain the words" in e.get('error', '')] + + if expected_valid: + assert len(reserved_word_errors) == 0 + else: + assert len(reserved_word_errors) == 1 + assert "Share class name cannot contain the words 'share', 'shares', or 'value'." in reserved_word_errors[0]['error'] + + +@pytest.mark.parametrize('share_class_name', [ + '', + ' ', + '\t', + '\n', +]) +def test_share_class_name_empty(session, share_class_name): + """Test that empty share class names are rejected.""" + share_class = { + 'name': share_class_name, + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': False, + 'series': [] + } + memoize_names = [] + + result = validate_shares(share_class, memoize_names, 'incorporationApplication', 0, 'BEN') + + assert len(result) >= 1 + assert any('Share class name is required' in e.get('error', '') for e in result) + + +@pytest.mark.parametrize('share_class_name', [ + ' Class A Shares', + 'Class A Shares ', + ' Class A Shares ', + '\tClass A Shares', + 'Class A Shares\n', +]) +def test_share_class_name_whitespace(session, share_class_name): + """Test that share class names with leading/trailing whitespace are rejected.""" + share_class = { + 'name': share_class_name, + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': False, + 'series': [] + } + memoize_names = [] + + result = validate_shares(share_class, memoize_names, 'incorporationApplication', 0, 'BEN') + + assert len(result) >= 1 + assert any('cannot start or end with whitespace' in e.get('error', '') for e in result) + + +def test_share_class_name_duplicate(session): + """Test that duplicate share class names are rejected.""" + share_class = { + 'name': 'Class A Shares', + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': False, + 'series': [] + } + memoize_names = ['Class A Shares'] # Already used + + result = validate_shares(share_class, memoize_names, 'incorporationApplication', 0, 'BEN') + + assert len(result) >= 1 + assert any('already used in a share class or series' in e.get('error', '') for e in result) + + +@pytest.mark.parametrize('reserved_word', EXCLUDED_WORDS_FOR_CLASS) +def test_share_class_name_each_reserved_word(session, reserved_word): + """Test that each reserved word is properly rejected in share class names.""" + share_class_name = f'{reserved_word.capitalize()} Type Shares' + share_class = { + 'name': share_class_name, + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': False, + 'series': [] + } + memoize_names = [] + + result = validate_shares(share_class, memoize_names, 'incorporationApplication', 0, 'BEN') + + reserved_word_errors = [e for e in result if "cannot contain the words" in e.get('error', '')] + assert len(reserved_word_errors) == 1 + + +@pytest.mark.parametrize('share_class_name', [ + 'SHARE Type Shares', + 'share Type Shares', + 'Share Type Shares', + 'ShArE Type Shares', + 'VALUE Class Shares', + 'value Class Shares', + 'Value Class Shares', +]) +def test_share_class_name_reserved_word_case_insensitive(session, share_class_name): + """Test that reserved word checking is case insensitive.""" + share_class = { + 'name': share_class_name, + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': False, + 'series': [] + } + memoize_names = [] + + result = validate_shares(share_class, memoize_names, 'incorporationApplication', 0, 'BEN') + + reserved_word_errors = [e for e in result if "cannot contain the words" in e.get('error', '')] + assert len(reserved_word_errors) == 1, f"Failed for: {share_class_name}" + + +@pytest.mark.parametrize('series_name, expected_valid', [ + # Valid names - end with " Shares" and no reserved words + ('Series A Shares', True), + ('Series 1 Shares', True), + ('Preferred Shares', True), + ('Class A Series 1 Shares', True), + ('Convertible Shares', True), + ('Non-Voting Shares', True), + # Invalid names - don't end with " Shares" + ('Series A', False), + ('Series 1', False), + ('Preferred', False), +]) +def test_series_name_must_end_with_shares(session, series_name, expected_valid): + """Test that series names must end with ' Shares'.""" + share_class = { + 'name': 'Class A Shares', + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': True, + 'series': [{ + 'name': series_name, + 'hasMaximumShares': False + }] + } + memoize_names = ['Class A Shares'] + + result = validate_series(share_class, memoize_names, 'incorporationApplication', 0) + + suffix_errors = [e for e in result if "must end with ' Shares'" in e.get('error', '')] + + if expected_valid: + assert len(suffix_errors) == 0 + else: + assert len(suffix_errors) == 1 + assert f"Share series name '{series_name}' must end with ' Shares'." in suffix_errors[0]['error'] + + +@pytest.mark.parametrize('series_name, expected_valid', [ + # Valid names - no reserved words (with " Shares" suffix) + ('Series A Shares', True), + ('Series 1 Shares', True), + ('Preferred Shares', True), + ('Class A Series 1 Shares', True), + ('Convertible Shares', True), + ('Non-Voting Shares', True), + # Invalid names - contain reserved word "share" (with " Shares" suffix) + ('Share Series A Shares', False), + ('Series Share 1 Shares', False), + ('My Share Shares', False), + # Invalid names - contain reserved word "shares" (with " Shares" suffix) + ('Shares Series Shares', False), + ('Series Shares Type Shares', False), + ('Multiple Shares Shares', False), +]) +def test_series_name_reserved_words(session, series_name, expected_valid): + """Test that series names cannot contain reserved words.""" + share_class = { + 'name': 'Class A Shares', + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': True, + 'series': [{ + 'name': series_name, + 'hasMaximumShares': False + }] + } + memoize_names = ['Class A Shares'] + + result = validate_series(share_class, memoize_names, 'incorporationApplication', 0) + + reserved_word_errors = [e for e in result if "cannot contain the words 'share' or 'shares'" in e.get('error', '')] + + if expected_valid: + assert len(reserved_word_errors) == 0 + else: + assert len(reserved_word_errors) == 1 + + +@pytest.mark.parametrize('series_name', [ + '', + ' ', + '\t', + '\n', +]) +def test_series_name_empty(session, series_name): + """Test that empty series names are rejected.""" + share_class = { + 'name': 'Class A Shares', + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': True, + 'series': [{ + 'name': series_name, + 'hasMaximumShares': False + }] + } + memoize_names = ['Class A Shares'] + + result = validate_series(share_class, memoize_names, 'incorporationApplication', 0) + + assert len(result) >= 1 + assert any('Share series name is required' in e.get('error', '') for e in result) + + +@pytest.mark.parametrize('series_name', [ + ' Series A', + 'Series A ', + ' Series A ', + '\tSeries A', + 'Series A\n', +]) +def test_series_name_whitespace(session, series_name): + """Test that series names with leading/trailing whitespace are rejected.""" + share_class = { + 'name': 'Class A Shares', + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': True, + 'series': [{ + 'name': series_name, + 'hasMaximumShares': False + }] + } + memoize_names = ['Class A Shares'] + + result = validate_series(share_class, memoize_names, 'incorporationApplication', 0) + + assert len(result) >= 1 + assert any('cannot start or end with whitespace' in e.get('error', '') for e in result) + + +def test_series_name_duplicate(session): + """Test that duplicate series names are rejected.""" + share_class = { + 'name': 'Class A Shares', + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': True, + 'series': [{ + 'name': 'Series A Shares', + 'hasMaximumShares': False + }] + } + memoize_names = ['Class A Shares', 'Series A Shares'] # Series A Shares already used + + result = validate_series(share_class, memoize_names, 'incorporationApplication', 0) + + assert len(result) >= 1 + assert any('already used in a share class or series' in e.get('error', '') for e in result) + + +@pytest.mark.parametrize('reserved_word', EXCLUDED_WORDS_FOR_SERIES) +def test_series_name_each_reserved_word(session, reserved_word): + """Test that each reserved word is properly rejected in series names.""" + series_name = f'{reserved_word.capitalize()} Series Shares' + share_class = { + 'name': 'Class A Shares', + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': True, + 'series': [{ + 'name': series_name, + 'hasMaximumShares': False + }] + } + memoize_names = ['Class A Shares'] + + result = validate_series(share_class, memoize_names, 'incorporationApplication', 0) + + reserved_word_errors = [e for e in result if "cannot contain the words 'share' or 'shares'" in e.get('error', '')] + assert len(reserved_word_errors) == 1 + + +@pytest.mark.parametrize('series_name', [ + 'SHARE Series Shares', + 'share Series Shares', + 'Share Series Shares', + 'ShArE Series Shares', + 'SHARES Series Shares', + 'shares Series Shares', + 'Shares Series Shares', +]) +def test_series_name_reserved_word_case_insensitive(session, series_name): + """Test that reserved word checking is case insensitive for series names.""" + share_class = { + 'name': 'Class A Shares', + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': True, + 'series': [{ + 'name': series_name, + 'hasMaximumShares': False + }] + } + memoize_names = ['Class A Shares'] + + result = validate_series(share_class, memoize_names, 'incorporationApplication', 0) + + reserved_word_errors = [e for e in result if "cannot contain the words 'share' or 'shares'" in e.get('error', '')] + assert len(reserved_word_errors) == 1, f"Failed for: {series_name}" + + +def test_series_allows_value_word(session): + """Test that series names CAN contain 'value' (only restricted for classes).""" + share_class = { + 'name': 'Class A Shares', + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': True, + 'series': [{ + 'name': 'Value Series Shares', + 'hasMaximumShares': False + }] + } + memoize_names = ['Class A Shares'] + + result = validate_series(share_class, memoize_names, 'incorporationApplication', 0) + + reserved_word_errors = [e for e in result if "cannot contain" in e.get('error', '')] + assert len(reserved_word_errors) == 0 + + +def test_valid_share_structure(session): + """Test a completely valid share structure.""" + filing_json = { + 'filing': { + 'incorporationApplication': { + 'shareStructure': { + 'shareClasses': [{ + 'name': 'Class A Shares', + 'hasMaximumShares': True, + 'maxNumberOfShares': 10000, + 'hasParValue': False, + 'hasRightsOrRestrictions': True, + 'series': [{ + 'name': 'Series 1 Shares', + 'hasMaximumShares': True, + 'maxNumberOfShares': 5000 + }] + }, { + 'name': 'Class B Shares', + 'hasMaximumShares': False, + 'hasParValue': True, + 'parValue': 1.00, + 'currency': 'CAD', + 'hasRightsOrRestrictions': False, + 'series': [] + }] + } + } + } + } + + result = validate_share_structure(filing_json, 'incorporationApplication', 'BEN') + + assert result is None + + +def test_share_structure_with_invalid_class_name_no_shares_suffix(session): + """Test share structure with class name missing ' Shares' suffix.""" + filing_json = { + 'filing': { + 'incorporationApplication': { + 'shareStructure': { + 'shareClasses': [{ + 'name': 'Class A', + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': False, + 'series': [] + }] + } + } + } + } + + result = validate_share_structure(filing_json, 'incorporationApplication', 'BEN') + + assert result is not None + assert any("must end with ' Shares'" in e.get('error', '') for e in result) + + +def test_share_structure_with_reserved_word_in_class_name(session): + """Test share structure with reserved word in class name.""" + filing_json = { + 'filing': { + 'incorporationApplication': { + 'shareStructure': { + 'shareClasses': [{ + 'name': 'Share Class Shares', + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': False, + 'series': [] + }] + } + } + } + } + + result = validate_share_structure(filing_json, 'incorporationApplication', 'BEN') + + assert result is not None + assert any("cannot contain the words 'share', 'shares', or 'value'" in e.get('error', '') for e in result) + + +def test_share_structure_with_reserved_word_in_series_name(session): + """Test share structure with reserved word in series name.""" + filing_json = { + 'filing': { + 'incorporationApplication': { + 'shareStructure': { + 'shareClasses': [{ + 'name': 'Class A Shares', + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': True, + 'series': [{ + 'name': 'Share Series 1 Shares', + 'hasMaximumShares': False + }] + }] + } + } + } + } + + result = validate_share_structure(filing_json, 'incorporationApplication', 'BEN') + + assert result is not None + assert any("cannot contain the words 'share' or 'shares'" in e.get('error', '') for e in result) + + +def test_share_structure_multiple_errors(session): + """Test share structure validation catches multiple errors.""" + filing_json = { + 'filing': { + 'incorporationApplication': { + 'shareStructure': { + 'shareClasses': [{ + 'name': 'Value Class', # Missing " Shares" and contains "value" + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': True, + 'series': [{ + 'name': 'Shares Series Shares', # Contains "shares" + 'hasMaximumShares': False + }] + }] + } + } + } + } + + result = validate_share_structure(filing_json, 'incorporationApplication', 'BEN') + + assert result is not None + assert len(result) >= 2 # At least errors for class name and series name + + +def test_share_structure_duplicate_names_between_class_and_series(session): + """Test that a series cannot have the same name as a class.""" + filing_json = { + 'filing': { + 'incorporationApplication': { + 'shareStructure': { + 'shareClasses': [{ + 'name': 'Class A Shares', + 'hasMaximumShares': False, + 'hasParValue': False, + 'hasRightsOrRestrictions': True, + 'series': [{ + 'name': 'Class A Shares', # Same as class name + 'hasMaximumShares': False + }] + }] + } + } + } + } + + result = validate_share_structure(filing_json, 'incorporationApplication', 'BEN') + + assert result is not None + assert any('already used' in e.get('error', '') for e in result) + + +def test_share_structure_empty_share_classes_for_ia(session): + """Test that incorporation application requires at least one share class.""" + filing_json = { + 'filing': { + 'incorporationApplication': { + 'shareStructure': { + 'shareClasses': [] + } + } + } + } + + result = validate_share_structure(filing_json, 'incorporationApplication', 'BEN') + + assert result is not None + assert len(result) == 1 + assert result[0]['error'] == 'A company must have at least one Class of Shares.' + assert result[0]['path'] == '/filing/incorporationApplication/shareStructure/shareClasses' + + +def test_share_structure_empty_share_classes_allowed_for_non_ia(session): + """Test that empty share classes are allowed for non-IA filings (e.g., alteration).""" + filing_json = { + 'filing': { + 'alteration': { + 'shareStructure': { + 'shareClasses': [] + } + } + } + } + + result = validate_share_structure(filing_json, 'alteration', 'BEN') + + assert result is None + +@pytest.mark.parametrize("max_shares,expected_error", [ + (None, "must provide value for maximum number of shares"), + ("1000", "Must be a whole number"), + (10.5, "Must be a whole number"), + (True, "Must be a whole number"), + (0, "Number must be greater than 0"), + (-5, "Number must be greater than 0"), + (10**16, "Number must be less than 16 digits"), +]) +def test_share_class_max_number_of_shares_validation(session, max_shares, expected_error): + share_class = { + 'name': 'Class A Shares', + 'hasMaximumShares': True, + 'maxNumberOfShares': max_shares, + 'hasParValue': False, + 'hasRightsOrRestrictions': False, + 'series': [] + } + memoize_names = [] + result = validate_shares(share_class, memoize_names, 'incorporationApplication', 0, 'BEN') + assert any(expected_error in e.get('error', '') for e in result) + +@pytest.mark.parametrize("max_shares,expected_error", [ + (None, "must provide value for maximum number of shares"), + ("1000", "Must be a whole number"), + (10.5, "Must be a whole number"), + (True, "Must be a whole number"), + (0, "Number must be greater than 0"), + (-5, "Number must be greater than 0"), + (10**16, "Number must be less than 16 digits"), +]) +def test_share_series_max_number_of_shares_validation(session, max_shares, expected_error): + share_class = { + 'name': 'Class A Shares', + 'hasMaximumShares': True, + 'maxNumberOfShares': 10000, + 'hasParValue': False, + 'hasRightsOrRestrictions': True, + 'series': [{ + 'name': 'Series 1 Shares', + 'hasMaximumShares': True, + 'maxNumberOfShares': max_shares + }] + } + memoize_names = ['Class A Shares'] + result = validate_series(share_class, memoize_names, 'incorporationApplication', 0) + assert any(expected_error in e.get('error', '') for e in result) + diff --git a/legal-api/tests/unit/services/filings/validations/test_continuation_in.py b/legal-api/tests/unit/services/filings/validations/test_continuation_in.py index dd74ae24e4..ee258b6d19 100644 --- a/legal-api/tests/unit/services/filings/validations/test_continuation_in.py +++ b/legal-api/tests/unit/services/filings/validations/test_continuation_in.py @@ -403,236 +403,236 @@ def test_validate_continuation_in_office(session, mocker, test_name, legal_type, 'class_name_2,series_name_2,' 'expected_code, expected_msg', [ - ('SUCCESS', 'CBEN', 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'CBEN', 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'CBEN', 'Share Class 1', False, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'CBEN', 'Class 1 Shares', False, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'CBEN', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, + ('SUCCESS', 'CBEN', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, None, None, None, None), - ('SUCCESS-CLASS2', 'CBEN', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', None, None, None), + ('SUCCESS-CLASS2', 'CBEN', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', None, None, None), ('FAIL-CLASS2', 'CBEN', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 1', None, + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 1 Shares', None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 name already used in a share class or series.', + 'error': 'Share class Class 1 Shares name already used in a share class or series.', 'path': '/filing/continuationIn/shareClasses/1/name/' }]), ('FAIL-SERIES2', 'CBEN', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', 'Share Series 1', + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', 'Series 1 Shares', HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 name already used in a share class or series.', + 'error': 'Share series Series 1 Shares name already used in a share class or series.', 'path': '/filing/continuationIn/shareClasses/0/series/1' }]), ('FAIL_INVALID_CLASS_MAX_SHARES', 'CBEN', - 'Share Class 1', True, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must provide value for maximum number of shares', + 'error': 'Share class Class 1 Shares must provide value for maximum number of shares', 'path': '/filing/continuationIn/shareClasses/0/maxNumberOfShares/' }]), ('FAIL_INVALID_CURRENCY', 'CBEN', - 'Share Class 1', True, 5000, True, 0.875, None, 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, 0.875, None, 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify currency', + 'error': 'Share class Class 1 Shares must specify currency', 'path': '/filing/continuationIn/shareClasses/0/currency/' }]), ('FAIL_INVALID_PAR_VALUE', 'CBEN', - 'Share Class 1', True, 5000, True, None, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, None, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify par value', + 'error': 'Share class Class 1 Shares must specify par value', 'path': '/filing/continuationIn/shareClasses/0/parValue/' }]), ('FAIL_INVALID_SERIES_MAX_SHARES', 'CBEN', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, None, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, None, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 must provide value for maximum number of shares', + 'error': 'Share series Series 1 Shares must provide value for maximum number of shares', 'path': '/filing/continuationIn/shareClasses/0/series/0/maxNumberOfShares' }]), ('FAIL_SERIES_SHARES_EXCEEDS_CLASS_SHARES', 'CBEN', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 10000, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 10000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': - 'Series Share Series 1 share quantity must be less than or equal to that of its class Share Class 1', + 'Series Series 1 Shares share quantity must be less than or equal to that of its class Class 1 Shares', 'path': '/filing/continuationIn/shareClasses/0/series/0/maxNumberOfShares' }]), - ('SUCCESS', 'C', 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'C', 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'C', 'Share Class 1', False, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'C', 'Class 1 Shares', False, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'C', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, + ('SUCCESS', 'C', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, None, None, None, None), - ('SUCCESS-CLASS2', 'C', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', None, None, None), + ('SUCCESS-CLASS2', 'C', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', None, None, None), ('FAIL-CLASS2', 'C', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 1', None, + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 1 Shares', None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 name already used in a share class or series.', + 'error': 'Share class Class 1 Shares name already used in a share class or series.', 'path': '/filing/continuationIn/shareClasses/1/name/' }]), ('FAIL-SERIES2', 'C', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', 'Share Series 1', + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', 'Series 1 Shares', HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 name already used in a share class or series.', + 'error': 'Share series Series 1 Shares name already used in a share class or series.', 'path': '/filing/continuationIn/shareClasses/0/series/1' }]), ('FAIL_INVALID_CLASS_MAX_SHARES', 'C', - 'Share Class 1', True, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must provide value for maximum number of shares', + 'error': 'Share class Class 1 Shares must provide value for maximum number of shares', 'path': '/filing/continuationIn/shareClasses/0/maxNumberOfShares/' }]), ('FAIL_INVALID_CURRENCY', 'C', - 'Share Class 1', True, 5000, True, 0.875, None, 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, 0.875, None, 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify currency', + 'error': 'Share class Class 1 Shares must specify currency', 'path': '/filing/continuationIn/shareClasses/0/currency/' }]), ('FAIL_INVALID_PAR_VALUE', 'C', - 'Share Class 1', True, 5000, True, None, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, None, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify par value', + 'error': 'Share class Class 1 Shares must specify par value', 'path': '/filing/continuationIn/shareClasses/0/parValue/' }]), ('FAIL_INVALID_SERIES_MAX_SHARES', 'C', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, None, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, None, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 must provide value for maximum number of shares', + 'error': 'Share series Series 1 Shares must provide value for maximum number of shares', 'path': '/filing/continuationIn/shareClasses/0/series/0/maxNumberOfShares' }]), ('FAIL_SERIES_SHARES_EXCEEDS_CLASS_SHARES', 'C', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 10000, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 10000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': - 'Series Share Series 1 share quantity must be less than or equal to that of its class Share Class 1', + 'Series Series 1 Shares share quantity must be less than or equal to that of its class Class 1 Shares', 'path': '/filing/continuationIn/shareClasses/0/series/0/maxNumberOfShares' }]), - ('SUCCESS', 'CUL', 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'CUL', 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'CUL', 'Share Class 1', False, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'CUL', 'Class 1 Shares', False, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'CUL', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, + ('SUCCESS', 'CUL', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, None, None, None, None), - ('SUCCESS-CLASS2', 'CUL', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', None, None, None), + ('SUCCESS-CLASS2', 'CUL', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', None, None, None), ('FAIL-CLASS2', 'CUL', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 1', None, + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 1 Shares', None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 name already used in a share class or series.', + 'error': 'Share class Class 1 Shares name already used in a share class or series.', 'path': '/filing/continuationIn/shareClasses/1/name/' }]), ('FAIL-SERIES2', 'CUL', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', 'Share Series 1', + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', 'Series 1 Shares', HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 name already used in a share class or series.', + 'error': 'Share series Series 1 Shares name already used in a share class or series.', 'path': '/filing/continuationIn/shareClasses/0/series/1' }]), ('FAIL_INVALID_CLASS_MAX_SHARES', 'CUL', - 'Share Class 1', True, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must provide value for maximum number of shares', + 'error': 'Share class Class 1 Shares must provide value for maximum number of shares', 'path': '/filing/continuationIn/shareClasses/0/maxNumberOfShares/' }]), ('FAIL_INVALID_CURRENCY', 'CUL', - 'Share Class 1', True, 5000, True, 0.875, None, 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, 0.875, None, 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify currency', + 'error': 'Share class Class 1 Shares must specify currency', 'path': '/filing/continuationIn/shareClasses/0/currency/' }]), ('FAIL_INVALID_PAR_VALUE', 'CUL', - 'Share Class 1', True, 5000, True, None, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, None, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify par value', + 'error': 'Share class Class 1 Shares must specify par value', 'path': '/filing/continuationIn/shareClasses/0/parValue/' }]), ('FAIL_INVALID_SERIES_MAX_SHARES', 'CUL', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, None, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, None, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 must provide value for maximum number of shares', + 'error': 'Share series Series 1 Shares must provide value for maximum number of shares', 'path': '/filing/continuationIn/shareClasses/0/series/0/maxNumberOfShares' }]), ('FAIL_SERIES_SHARES_EXCEEDS_CLASS_SHARES', 'CUL', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 10000, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 10000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': - 'Series Share Series 1 share quantity must be less than or equal to that of its class Share Class 1', + 'Series Series 1 Shares share quantity must be less than or equal to that of its class Class 1 Shares', 'path': '/filing/continuationIn/shareClasses/0/series/0/maxNumberOfShares' }]), - ('SUCCESS', 'CCC', 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'CCC', 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'CCC', 'Share Class 1', False, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'CCC', 'Class 1 Shares', False, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'CCC', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, + ('SUCCESS', 'CCC', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, None, None, None, None), - ('SUCCESS-CLASS2', 'CCC', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', None, None, None), + ('SUCCESS-CLASS2', 'CCC', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', None, None, None), ('FAIL-CLASS2', 'CCC', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 1', None, + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 1 Shares', None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 name already used in a share class or series.', + 'error': 'Share class Class 1 Shares name already used in a share class or series.', 'path': '/filing/continuationIn/shareClasses/1/name/' }]), ('FAIL-SERIES2', 'CCC', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', 'Share Series 1', + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', 'Series 1 Shares', HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 name already used in a share class or series.', + 'error': 'Share series Series 1 Shares name already used in a share class or series.', 'path': '/filing/continuationIn/shareClasses/0/series/1' }]), ('FAIL_INVALID_CLASS_MAX_SHARES', 'CCC', - 'Share Class 1', True, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must provide value for maximum number of shares', + 'error': 'Share class Class 1 Shares must provide value for maximum number of shares', 'path': '/filing/continuationIn/shareClasses/0/maxNumberOfShares/' }]), ('FAIL_INVALID_CURRENCY', 'CCC', - 'Share Class 1', True, 5000, True, 0.875, None, 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, 0.875, None, 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify currency', + 'error': 'Share class Class 1 Shares must specify currency', 'path': '/filing/continuationIn/shareClasses/0/currency/' }]), ('FAIL_INVALID_PAR_VALUE', 'CCC', - 'Share Class 1', True, 5000, True, None, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, None, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify par value', + 'error': 'Share class Class 1 Shares must specify par value', 'path': '/filing/continuationIn/shareClasses/0/parValue/' }]), ('FAIL_INVALID_SERIES_MAX_SHARES', 'CCC', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, None, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, None, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 must provide value for maximum number of shares', + 'error': 'Share series Series 1 Shares must provide value for maximum number of shares', 'path': '/filing/continuationIn/shareClasses/0/series/0/maxNumberOfShares' }]), ('FAIL_SERIES_SHARES_EXCEEDS_CLASS_SHARES', 'CCC', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 10000, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 10000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': - 'Series Share Series 1 share quantity must be less than or equal to that of its class Share Class 1', + 'Series Series 1 Shares share quantity must be less than or equal to that of its class Class 1 Shares', 'path': '/filing/continuationIn/shareClasses/0/series/0/maxNumberOfShares' }]) ]) @@ -660,7 +660,7 @@ def test_validate_continuation_in_share_classes(session, mocker, test_name, lega share_structure = filing['filing']['continuationIn']['shareStructure'] share_structure['shareClasses'].append({ 'id': 2, - 'name': 'Share Class 2', + 'name': 'Class 2 Shares', 'priority': 1, 'hasMaximumShares': False, 'maxNumberOfShares': None, diff --git a/legal-api/tests/unit/services/filings/validations/test_incorporation_application.py b/legal-api/tests/unit/services/filings/validations/test_incorporation_application.py index 54cee595ba..ab2faab707 100644 --- a/legal-api/tests/unit/services/filings/validations/test_incorporation_application.py +++ b/legal-api/tests/unit/services/filings/validations/test_incorporation_application.py @@ -259,6 +259,9 @@ def test_validate_incorporation_addresses_basic(session, mocker, test_name, lega mocker.patch('legal_api.services.filings.validations.incorporation_application.validate_roles', return_value=[]) + mocker.patch('legal_api.services.filings.validations.incorporation_application.validate_share_structure', + return_value=None) + # perform test with freeze_time(now): err = validate(business, filing_json) @@ -1279,266 +1282,266 @@ def test_validate_incorporation_party_names(session, mocker, test_name, 'class_name_2,series_name_2,' 'expected_code, expected_msg', [ - ('SUCCESS', 'BEN', 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'BEN', 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'BEN', 'Share Class 1', False, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'BEN', 'Class 1 Shares', False, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'BEN', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, + ('SUCCESS', 'BEN', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, None, None, None, None), - ('SUCCESS-CLASS2', 'BEN', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', None, None, None), + ('SUCCESS-CLASS2', 'BEN', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', None, None, None), ('FAIL-CLASS2', 'BEN', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 1', None, + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 1 Shares', None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 name already used in a share class or series.', + 'error': 'Share class Class 1 Shares name already used in a share class or series.', 'path': '/filing/incorporationApplication/shareClasses/1/name/' }]), ('FAIL-SERIES2', 'BEN', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', 'Share Series 1', + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', 'Series 1 Shares', HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 name already used in a share class or series.', + 'error': 'Share series Series 1 Shares name already used in a share class or series.', 'path': '/filing/incorporationApplication/shareClasses/0/series/1' }]), - ('FAIL_EMPTY_CLASS_NAME', 'BEN', '', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('FAIL_EMPTY_CLASS_NAME', 'BEN', '', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': 'Share class name is required.', 'path': '/filing/incorporationApplication/shareClasses/0/name/' }]), - ('FAIL_WHITESPACE_ONLY_CLASS_NAME', 'BEN', ' ', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('FAIL_WHITESPACE_ONLY_CLASS_NAME', 'BEN', ' ', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': 'Share class name is required.', 'path': '/filing/incorporationApplication/shareClasses/0/name/' }]), - ('FAIL_LEADING_AND_TRAILING_WHITESPACE_CLASS_NAME', 'BEN', ' Share Series 1 ', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('FAIL_LEADING_AND_TRAILING_WHITESPACE_CLASS_NAME', 'BEN', ' Series 1 Shares ', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': 'Share class name cannot start or end with whitespace.', 'path': '/filing/incorporationApplication/shareClasses/0/name/' }]), - ('FAIL_EMPTY_SERIES_NAME', 'BEN', 'Share Class 1', True, 5000, True, 0.875, 'CAD', '', True, 1000, + ('FAIL_EMPTY_SERIES_NAME', 'BEN', 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', '', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': 'Share series name is required.', 'path': '/filing/incorporationApplication/shareClasses/0/series/0/name/' }]), - ('FAIL_WHITESPACE_ONLY_SERIES_NAME', 'BEN', 'Share Class 1', True, 5000, True, 0.875, 'CAD', ' ', True, 1000, + ('FAIL_WHITESPACE_ONLY_SERIES_NAME', 'BEN', 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', ' ', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': 'Share series name is required.', 'path': '/filing/incorporationApplication/shareClasses/0/series/0/name/' }]), - ('FAIL_LEADING_AND_TRAILING_WHITESPACE_SERIES_NAME', 'BEN', 'Share Class 1', True, 5000, True, 0.875, 'CAD', ' Share Series 1 ', True, 1000, + ('FAIL_LEADING_AND_TRAILING_WHITESPACE_SERIES_NAME', 'BEN', 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', ' Series 1 Shares ', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': 'Share series name cannot start or end with whitespace.', 'path': '/filing/incorporationApplication/shareClasses/0/series/0/name/' }]), ('FAIL_INVALID_CLASS_MAX_SHARES', 'BEN', - 'Share Class 1', True, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must provide value for maximum number of shares', + 'error': 'Share class Class 1 Shares must provide value for maximum number of shares', 'path': '/filing/incorporationApplication/shareClasses/0/maxNumberOfShares/' }]), ('FAIL_INVALID_CURRENCY', 'BEN', - 'Share Class 1', True, 5000, True, 0.875, None, 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, 0.875, None, 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify currency', + 'error': 'Share class Class 1 Shares must specify currency', 'path': '/filing/incorporationApplication/shareClasses/0/currency/' }]), ('FAIL_INVALID_PAR_VALUE', 'BEN', - 'Share Class 1', True, 5000, True, None, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, None, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify par value', + 'error': 'Share class Class 1 Shares must specify par value', 'path': '/filing/incorporationApplication/shareClasses/0/parValue/' }]), ('FAIL_INVALID_SERIES_MAX_SHARES', 'BEN', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, None, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, None, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 must provide value for maximum number of shares', + 'error': 'Share series Series 1 Shares must provide value for maximum number of shares', 'path': '/filing/incorporationApplication/shareClasses/0/series/0/maxNumberOfShares' }]), ('FAIL_SERIES_SHARES_EXCEEDS_CLASS_SHARES', 'BEN', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 10000, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 10000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': - 'Series Share Series 1 share quantity must be less than or equal to that of its class Share Class 1', + 'Series Series 1 Shares share quantity must be less than or equal to that of its class Class 1 Shares', 'path': '/filing/incorporationApplication/shareClasses/0/series/0/maxNumberOfShares' }]), - ('SUCCESS', 'BC', 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'BC', 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'BC', 'Share Class 1', False, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'BC', 'Class 1 Shares', False, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'BC', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, + ('SUCCESS', 'BC', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, None, None, None, None), - ('SUCCESS-CLASS2', 'BC', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', None, None, None), + ('SUCCESS-CLASS2', 'BC', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', None, None, None), ('FAIL-CLASS2', 'BC', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 1', None, + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 1 Shares', None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 name already used in a share class or series.', + 'error': 'Share class Class 1 Shares name already used in a share class or series.', 'path': '/filing/incorporationApplication/shareClasses/1/name/' }]), ('FAIL-SERIES2', 'BC', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', 'Share Series 1', + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', 'Series 1 Shares', HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 name already used in a share class or series.', + 'error': 'Share series Series 1 Shares name already used in a share class or series.', 'path': '/filing/incorporationApplication/shareClasses/0/series/1' }]), ('FAIL_INVALID_CLASS_MAX_SHARES', 'BC', - 'Share Class 1', True, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must provide value for maximum number of shares', + 'error': 'Share class Class 1 Shares must provide value for maximum number of shares', 'path': '/filing/incorporationApplication/shareClasses/0/maxNumberOfShares/' }]), ('FAIL_INVALID_CURRENCY', 'BC', - 'Share Class 1', True, 5000, True, 0.875, None, 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, 0.875, None, 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify currency', + 'error': 'Share class Class 1 Shares must specify currency', 'path': '/filing/incorporationApplication/shareClasses/0/currency/' }]), ('FAIL_INVALID_PAR_VALUE', 'BC', - 'Share Class 1', True, 5000, True, None, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, None, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify par value', + 'error': 'Share class Class 1 Shares must specify par value', 'path': '/filing/incorporationApplication/shareClasses/0/parValue/' }]), ('FAIL_INVALID_SERIES_MAX_SHARES', 'BC', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, None, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, None, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 must provide value for maximum number of shares', + 'error': 'Share series Series 1 Shares must provide value for maximum number of shares', 'path': '/filing/incorporationApplication/shareClasses/0/series/0/maxNumberOfShares' }]), ('FAIL_SERIES_SHARES_EXCEEDS_CLASS_SHARES', 'BC', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 10000, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 10000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': - 'Series Share Series 1 share quantity must be less than or equal to that of its class Share Class 1', + 'Series Series 1 Shares share quantity must be less than or equal to that of its class Class 1 Shares', 'path': '/filing/incorporationApplication/shareClasses/0/series/0/maxNumberOfShares' }]), - ('SUCCESS', 'ULC', 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'ULC', 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'ULC', 'Share Class 1', False, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'ULC', 'Class 1 Shares', False, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'ULC', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, + ('SUCCESS', 'ULC', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, None, None, None, None), - ('SUCCESS-CLASS2', 'ULC', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', None, None, None), + ('SUCCESS-CLASS2', 'ULC', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', None, None, None), ('FAIL-CLASS2', 'ULC', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 1', None, + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 1 Shares', None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 name already used in a share class or series.', + 'error': 'Share class Class 1 Shares name already used in a share class or series.', 'path': '/filing/incorporationApplication/shareClasses/1/name/' }]), ('FAIL-SERIES2', 'ULC', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', 'Share Series 1', + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', 'Series 1 Shares', HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 name already used in a share class or series.', + 'error': 'Share series Series 1 Shares name already used in a share class or series.', 'path': '/filing/incorporationApplication/shareClasses/0/series/1' }]), ('FAIL_INVALID_CLASS_MAX_SHARES', 'ULC', - 'Share Class 1', True, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must provide value for maximum number of shares', + 'error': 'Share class Class 1 Shares must provide value for maximum number of shares', 'path': '/filing/incorporationApplication/shareClasses/0/maxNumberOfShares/' }]), ('FAIL_INVALID_CURRENCY', 'ULC', - 'Share Class 1', True, 5000, True, 0.875, None, 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, 0.875, None, 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify currency', + 'error': 'Share class Class 1 Shares must specify currency', 'path': '/filing/incorporationApplication/shareClasses/0/currency/' }]), ('FAIL_INVALID_PAR_VALUE', 'ULC', - 'Share Class 1', True, 5000, True, None, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, None, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify par value', + 'error': 'Share class Class 1 Shares must specify par value', 'path': '/filing/incorporationApplication/shareClasses/0/parValue/' }]), ('FAIL_INVALID_SERIES_MAX_SHARES', 'ULC', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, None, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, None, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 must provide value for maximum number of shares', + 'error': 'Share series Series 1 Shares must provide value for maximum number of shares', 'path': '/filing/incorporationApplication/shareClasses/0/series/0/maxNumberOfShares' }]), ('FAIL_SERIES_SHARES_EXCEEDS_CLASS_SHARES', 'ULC', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 10000, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 10000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': - 'Series Share Series 1 share quantity must be less than or equal to that of its class Share Class 1', + 'Series Series 1 Shares share quantity must be less than or equal to that of its class Class 1 Shares', 'path': '/filing/incorporationApplication/shareClasses/0/series/0/maxNumberOfShares' }]), - ('SUCCESS', 'CC', 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'CC', 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'CC', 'Share Class 1', False, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + ('SUCCESS', 'CC', 'Class 1 Shares', False, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, None, None), - ('SUCCESS', 'CC', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, + ('SUCCESS', 'CC', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, None, None, None, None), - ('SUCCESS-CLASS2', 'CC', 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', None, None, None), + ('SUCCESS-CLASS2', 'CC', 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', None, None, None), ('FAIL-CLASS2', 'CC', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 1', None, + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 1 Shares', None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 name already used in a share class or series.', + 'error': 'Share class Class 1 Shares name already used in a share class or series.', 'path': '/filing/incorporationApplication/shareClasses/1/name/' }]), ('FAIL-SERIES2', 'CC', - 'Share Class 1', False, None, False, None, None, 'Share Series 1', False, None, - 'Share Class 2', 'Share Series 1', + 'Class 1 Shares', False, None, False, None, None, 'Series 1 Shares', False, None, + 'Class 2 Shares', 'Series 1 Shares', HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 name already used in a share class or series.', + 'error': 'Share series Series 1 Shares name already used in a share class or series.', 'path': '/filing/incorporationApplication/shareClasses/0/series/1' }]), ('FAIL_INVALID_CLASS_MAX_SHARES', 'CC', - 'Share Class 1', True, None, True, 0.875, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, None, True, 0.875, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must provide value for maximum number of shares', + 'error': 'Share class Class 1 Shares must provide value for maximum number of shares', 'path': '/filing/incorporationApplication/shareClasses/0/maxNumberOfShares/' }]), ('FAIL_INVALID_CURRENCY', 'CC', - 'Share Class 1', True, 5000, True, 0.875, None, 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, 0.875, None, 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify currency', + 'error': 'Share class Class 1 Shares must specify currency', 'path': '/filing/incorporationApplication/shareClasses/0/currency/' }]), ('FAIL_INVALID_PAR_VALUE', 'CC', - 'Share Class 1', True, 5000, True, None, 'CAD', 'Share Series 1', True, 1000, + 'Class 1 Shares', True, 5000, True, None, 'CAD', 'Series 1 Shares', True, 1000, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share class Share Class 1 must specify par value', + 'error': 'Share class Class 1 Shares must specify par value', 'path': '/filing/incorporationApplication/shareClasses/0/parValue/' }]), ('FAIL_INVALID_SERIES_MAX_SHARES', 'CC', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, None, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, None, None, None, HTTPStatus.BAD_REQUEST, [{ - 'error': 'Share series Share Series 1 must provide value for maximum number of shares', + 'error': 'Share series Series 1 Shares must provide value for maximum number of shares', 'path': '/filing/incorporationApplication/shareClasses/0/series/0/maxNumberOfShares' }]), ('FAIL_SERIES_SHARES_EXCEEDS_CLASS_SHARES', 'CC', - 'Share Class 1', True, 5000, True, 0.875, 'CAD', 'Share Series 1', True, 10000, + 'Class 1 Shares', True, 5000, True, 0.875, 'CAD', 'Series 1 Shares', True, 10000, None, None, HTTPStatus.BAD_REQUEST, [{ 'error': - 'Series Share Series 1 share quantity must be less than or equal to that of its class Share Class 1', + 'Series Series 1 Shares share quantity must be less than or equal to that of its class Class 1 Shares', 'path': '/filing/incorporationApplication/shareClasses/0/series/0/maxNumberOfShares' }]) ]) @@ -1606,6 +1609,10 @@ def test_validate_incorporation_share_classes(session, mocker, test_name, legal_ # validate outcomes if expected_code: assert err.code == expected_code + # print the expected vs actual messages for easier debugging + print('Expected Msg:', expected_msg) + print('Actual Msg:', err.msg) + assert lists_are_equal(err.msg, expected_msg) else: assert err is None @@ -2070,4 +2077,30 @@ def test_incorporation_permission_and_completing_party_flag(mocker, app, session assert expected_msg in str(err.msg[0].get('message', err.msg[0].get('error', ''))) else: assert err is None + + +def test_coop_incorporation_does_not_validate_shares(session, mocker): + """Assert that COOP incorporation does not call validate_share_structure.""" + filing_json = copy.deepcopy(INCORPORATION_FILING_TEMPLATE) + filing_json['filing']['header'] = {'name': 'incorporationApplication', 'date': '2019-04-08', + 'certifiedBy': 'full name', 'email': 'no_one@never.get', 'filingId': 1} + filing_json['filing']['incorporationApplication'] = copy.deepcopy(INCORPORATION) + filing_json['filing']['incorporationApplication']['nameRequest']['legalType'] = Business.LegalTypes.COOP.value + + # Mock all other validations to isolate the test + for func_name in ['validate_offices', 'validate_offices_addresses', 'validate_roles', + 'validate_parties_names', 'validate_parties_addresses', + 'validate_coop_parties_mailing_address', 'validate_parties_delivery_address', + 'validate_name_request', 'validate_cooperative_documents', 'validate_effective_date', + 'validate_ia_court_order', 'validate_phone_number', 'validate_email', + 'validate_name_translation']: + mocker.patch.object(incorporation_application, func_name, return_value=[]) + mocker.patch.object(flags, 'is_on', return_value=False) + + mock_share_structure = mocker.patch.object(incorporation_application, 'validate_share_structure') + + err = incorporation_application.validate(filing_json) + + mock_share_structure.assert_not_called() + assert err is None \ No newline at end of file