Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions cooperator/models/operation_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,26 @@ def validate(self):
_("The cooperator can't hand over more shares that he/she owns.")
)

if self.share_product_id.product_tmpl_id.force_min_qty:
resulting_share_qty = (
total_share_dic[self.share_product_id.id] - self.quantity
)
minimal_share_qty = (
self.share_product_id.product_tmpl_id.minimum_quantity
)
if resulting_share_qty != 0 and resulting_share_qty < minimal_share_qty:
raise ValidationError(
_(
"This share type only accept at least {minimal_share_qty} shares. "
"This cooperator can't sell {quantity} share or he will only "
"have {resulting_share_qty} left."
).format(
minimal_share_qty=minimal_share_qty,
quantity=self.quantity,
resulting_share_qty=resulting_share_qty,
)
)

if self.operation_type == "convert":
if self.company_id.unmix_share_type:
if self.share_product_id.code == self.share_to_product_id.code:
Expand Down
27 changes: 27 additions & 0 deletions cooperator/models/subscription_request.py
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding a small (python) test to cover these 2 cases would be nice.

Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,17 @@ def validate_subscription_request(self):
if self.ordered_parts <= 0:
raise UserError(_("Number of share must be greater than 0."))

if (
self.share_product_id.product_tmpl_id.force_min_qty
and self.ordered_parts
< self.share_product_id.product_tmpl_id.minimum_quantity
):
raise UserError(
_("Number of shares must be at least {min_qty}").format(
min_qty=self.share_product_id.product_tmpl_id.minimum_quantity
)
)

partner = self.setup_partner()

invoice = self.create_invoice(partner)
Expand Down Expand Up @@ -896,3 +907,19 @@ def put_on_waiting_list(self):
)
self._send_waiting_list_mail()
self.write({"state": "waiting"})

@api.constrains("ordered_parts")
def _check_ordered_parts_quantity(self):
for sub_req in self:
min_qty = sub_req.share_product_id.product_tmpl_id.minimum_quantity
if sub_req.ordered_parts < min_qty:
raise ValidationError(
_(
"The number of shares must be more than the defined minimum "
"quantity of this share. For {share_name}, the minimum is "
"{minimum_quantity}."
).format(
share_name=sub_req.share_product_id.short_name,
minimum_quantity=min_qty,
)
)
4 changes: 3 additions & 1 deletion cooperator/tests/cooperator_test_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ def set_up_cooperator_test_data(cls):
"categ_id": company_share_category.id,
"is_share": True,
"default_share_product": True,
"force_min_qty": True,
"minimum_quantity": 2,
"by_individual": True,
"by_company": True,
"list_price": 25,
Expand Down Expand Up @@ -143,7 +145,7 @@ def create_payment_account_move(cls, invoice, date, amount=None):
def get_dummy_subscription_requests_vals(cls, **custom_vals):
vals = {
"share_product_id": cls.share_y.id,
"ordered_parts": 2,
"ordered_parts": 6,
"firstname": "first name",
"lastname": "last name",
"email": "email@example.net",
Expand Down
103 changes: 83 additions & 20 deletions cooperator/tests/test_cooperator.py
Original file line number Diff line number Diff line change
Expand Up @@ -849,8 +849,10 @@ def test_existing_partner_company_dependent_fields_with_membership(self):
self.assertFalse(partner.coop_candidate)
self.assertFalse(partner.old_member)
self.assertNotEqual(partner.cooperator_register_number, 0)
self.assertEqual(partner.number_of_share, 2)
self.assertEqual(partner.total_value, 50)
self.assertEqual(partner.number_of_share, vals["ordered_parts"])
self.assertEqual(
partner.total_value, vals["ordered_parts"] * self.share_y.list_price
)
self.assertEqual(partner.cooperator_type, "share_y")
self.assertEqual(partner.effective_date, date(2023, 6, 21))
# fixme: these should probably be true. see comment in
Expand Down Expand Up @@ -918,8 +920,10 @@ def test_partner_company_dependent_fields_with_membership(self):
self.assertFalse(partner.coop_candidate)
self.assertFalse(partner.old_member)
self.assertNotEqual(partner.cooperator_register_number, 0)
self.assertEqual(partner.number_of_share, 2)
self.assertEqual(partner.total_value, 50)
self.assertEqual(partner.number_of_share, vals["ordered_parts"])
self.assertEqual(
partner.total_value, vals["ordered_parts"] * self.share_y.list_price
)
self.assertEqual(partner.cooperator_type, "share_y")
self.assertEqual(partner.effective_date, date(2023, 6, 21))
self.assertTrue(partner.data_policy_approved)
Expand Down Expand Up @@ -1196,12 +1200,13 @@ def test_transfer_operation(self):
"source": "operation",
}
)
transfer_qty = 1
operation_request = self.env["operation.request"].create(
{
"operation_type": "transfer",
"partner_id": cooperator.id,
"share_product_id": self.share_y.id,
"quantity": 1,
"quantity": transfer_qty,
"receiver_not_member": True,
"subscription_request": [
fields.Command.create(subscription_request_vals)
Expand All @@ -1213,7 +1218,10 @@ def test_transfer_operation(self):
operation_request.approve_operation()
last_register_id = self._get_last_register_id()
operation_request.execute_operation()
self.assertEqual(cooperator.number_of_share, 1)
self.assertEqual(
cooperator.number_of_share,
subscription_request_vals["ordered_parts"] - transfer_qty,
)
new_cooperator = self.env["res.partner"].search(
[("email", "=", "email2@example.net")]
)
Expand Down Expand Up @@ -1254,18 +1262,57 @@ def test_transfer_operation_form(self):
)
for field in ["address", "zip_code", "city", "lang", "iban"]:
setattr(sr, field, subscription_request_vals[field])
transfer_quantity = 4
f.partner_id = cooperator
f.share_product_id = self.share_y
f.quantity = 1
f.quantity = transfer_quantity
operation_request = f.save()
self.assertEqual(operation_request.subscription_request.state, "transfer")
self.assertEqual(
operation_request.subscription_request.share_product_id, self.share_y
)
self.assertEqual(operation_request.subscription_request.ordered_parts, 1)
self.assertEqual(
operation_request.subscription_request.ordered_parts, transfer_quantity
)
operation_request.submit_operation()
operation_request.approve_operation()

@freeze_time("2023-06-21")
def test_transfer_operation_too_few_shares_left(self):
"""
Test that the subscription request created during a share transfer
operation fails if the number of shares left is smaller than the min.
"""
cooperator = self.create_dummy_cooperator()
f = Form(self.env["operation.request"])
f.operation_type = "transfer"
f.receiver_not_member = True
with f.subscription_request.new() as sr:
sr.firstname = "first name 2"
sr.lastname = "last name 2"
sr.email = "email2@example.net"
subscription_request_vals = self.get_dummy_subscription_requests_vals()
sr.country_id = self.env["res.country"].browse(
subscription_request_vals["country_id"]
)
for field in ["address", "zip_code", "city", "lang", "iban"]:
setattr(sr, field, subscription_request_vals[field])
transfer_quantity = 5
f.partner_id = cooperator
f.share_product_id = self.share_y
f.quantity = transfer_quantity
operation_request = f.save()
self.assertEqual(operation_request.subscription_request.state, "transfer")
self.assertEqual(
operation_request.subscription_request.share_product_id, self.share_y
)
self.assertEqual(
operation_request.subscription_request.ordered_parts, transfer_quantity
)
with self.assertRaises(ValidationError):
operation_request.submit_operation()
operation_request.approve_operation()

@freeze_time("2023-06-21")
def test_transfer_operation_existing_cooperator(self):
"""
Expand All @@ -1288,25 +1335,32 @@ def test_transfer_operation_existing_cooperator(self):
new_cooperator = self.env["res.partner"].search(
[("email", "=", "email2@example.net")]
)
transfered_quantity = 1
operation_request = self.env["operation.request"].create(
{
"operation_type": "transfer",
"partner_id": cooperator.id,
"partner_id_to": new_cooperator.id,
"share_product_id": self.share_y.id,
"quantity": 1,
"quantity": transfered_quantity,
}
)
operation_request.submit_operation()
operation_request.approve_operation()
last_register_id = self._get_last_register_id()
operation_request.execute_operation()
self.assertEqual(cooperator.number_of_share, 1)
self.assertEqual(new_cooperator.number_of_share, 3)
self.assertEqual(
cooperator.number_of_share,
subscription_request_vals["ordered_parts"] - transfered_quantity,
)
self.assertEqual(
new_cooperator.number_of_share,
subscription_request_vals["ordered_parts"] + transfered_quantity,
)
register_entry = self._get_new_register_records(last_register_id)
self.assertEqual(register_entry.partner_id, cooperator)
self.assertEqual(register_entry.partner_id_to, new_cooperator)
self.assertEqual(register_entry.quantity, 1)
self.assertEqual(register_entry.quantity, transfered_quantity)
self.assertEqual(register_entry.share_product_id, self.share_y)
self.assertEqual(register_entry.type, "transfer")
self.assertEqual(register_entry.share_unit_price, self.share_y.list_price)
Expand All @@ -1333,7 +1387,7 @@ def test_sell_back_operation(self):
operation_request.approve_operation()
last_register_id = self._get_last_register_id()
operation_request.execute_operation()
self.assertEqual(cooperator.number_of_share, 1)
self.assertEqual(cooperator.number_of_share, 5)
register_entry = self._get_new_register_records(last_register_id)
self.assertEqual(register_entry.partner_id, cooperator)
self.assertFalse(register_entry.partner_id_to)
Expand All @@ -1358,7 +1412,7 @@ def test_sell_back_all_shares(self):
"operation_type": "sell_back",
"partner_id": cooperator.id,
"share_product_id": self.share_y.id,
"quantity": 2,
"quantity": 6,
}
)
operation_request.submit_operation()
Expand All @@ -1373,33 +1427,36 @@ def test_convert_operation(self):
Test that the share conversion operation works correctly.
"""
cooperator = self.create_dummy_cooperator()
share_qty = self.get_dummy_subscription_requests_vals()["ordered_parts"]
operation_request = self.env["operation.request"].create(
{
"operation_type": "convert",
"partner_id": cooperator.id,
"share_product_id": self.share_y.id,
"share_to_product_id": self.share_x.id,
"quantity": 2,
"quantity": share_qty,
}
)
operation_request.submit_operation()
operation_request.approve_operation()
last_register_id = self._get_last_register_id()
operation_request.execute_operation()
# share_x costs twice as much as share_y, so there is only one share
self.assertEqual(cooperator.number_of_share, 1)
resulting_shares = share_qty * (
self.share_y.list_price / self.share_x.list_price
)
self.assertEqual(cooperator.number_of_share, resulting_shares)
self.assertEqual(cooperator.cooperator_type, "share_x")
share_line = cooperator.share_ids
self.assertEqual(share_line.share_product_id, self.share_x)
self.assertEqual(share_line.share_number, 1)
self.assertEqual(share_line.share_number, resulting_shares)
self.assertEqual(share_line.share_unit_price, self.share_x.list_price)
register_entry = self._get_new_register_records(last_register_id)
self.assertEqual(register_entry.partner_id, cooperator)
self.assertFalse(register_entry.partner_id_to)
self.assertEqual(register_entry.quantity, 2)
self.assertEqual(register_entry.quantity, share_qty)
self.assertEqual(register_entry.share_product_id, self.share_y)
self.assertEqual(register_entry.share_to_product_id, self.share_x)
self.assertEqual(register_entry.quantity_to, 1)
self.assertEqual(register_entry.quantity_to, resulting_shares)
self.assertEqual(register_entry.type, "convert")
self.assertEqual(register_entry.share_unit_price, self.share_y.list_price)
self.assertEqual(register_entry.date, date(2023, 6, 21))
Expand Down Expand Up @@ -1616,3 +1673,9 @@ def test_create_user_multiple_users(self):
self.assertEqual(inactive_user.company_ids, self.env.company)
self.assertEqual(inactive_user.company_id, self.env.company)
self.assertTrue(inactive_user.active)

def test_create_subscription_with_too_few_shares(self):
dummy_subscription_vals = self.get_dummy_subscription_requests_vals()
dummy_subscription_vals["ordered_parts"] = 1
with self.assertRaises(UserError):
self.env["subscription.request"].create(dummy_subscription_vals)
5 changes: 2 additions & 3 deletions cooperator/tests/test_mail_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,14 +303,13 @@ def _test_mail_template_share_transfer_all_shares(
}
)
last_mail_id = self._get_last_mail_id()
share_qty = self.get_dummy_subscription_requests_vals()["ordered_parts"]
operation_request = self.env["operation.request"].create(
{
"operation_type": "transfer",
"partner_id": cooperator.id,
"share_product_id": self.share_y.id,
# 2 is all the quality that the partner has.
"quantity": 2,
# TODO: this field should be computed or shouldn't exist.
"quantity": share_qty,
"receiver_not_member": True,
"subscription_request": [
fields.Command.create(subscription_request_vals)
Expand Down
21 changes: 21 additions & 0 deletions cooperator_website/controllers/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,14 @@ def _additional_validate(self, kwargs, logged, values, post_file):
"""
return True

def validate_minimum_quantity(self, kwargs):
share_product_id = int(kwargs.get("share_product_id"))
share_product = self.get_share_product(share_product_id)[share_product_id]
qty_share = int(kwargs.get("ordered_parts"))
return (
not share_product["force_min_qty"] or qty_share >= share_product["min_qty"]
)

def validation( # noqa: C901 (method too complex)
self, kwargs, logged, values, post_file
):
Expand Down Expand Up @@ -341,6 +349,14 @@ def validation( # noqa: C901 (method too complex)
values["error"] = {"iban"}
return request.render(redirect, values)

# check subscription respect min qty of shares
if not self.validate_minimum_quantity(kwargs):
values = self.fill_values(values, is_company, logged)
values["error_msg"] = _(
"Number of shares must be greater than {min_qty}"
).format(min_qty=self.get_share_product_min_quantity(kwargs))
return request.render(redirect, values)

# check the subscription's amount
max_amount = company.subscription_maximum_amount
if logged:
Expand Down Expand Up @@ -394,6 +410,11 @@ def get_share_product(self, share_product_id, **kw):
}
}

def get_share_product_min_quantity(self, kwargs):
share_product_id = int(kwargs.get("share_product_id"))
share_product = self.get_share_product(share_product_id)[share_product_id]
return share_product["min_qty"]

@http.route( # noqa: C901 (method too complex)
["/subscription/subscribe_share"],
type="http",
Expand Down
Loading