Dziu Gas
Dziu Gas

Reputation: 47

Python: Validate Regex, find a match inside string

We have an invoice numbering rule, which we would like to modify a bit.

Inside user settings, there's a setting that sets the user invoice numbering format, we can let the user to change there anything he wants, but we have an invoice creation page, inside the page what we only want to allow is to let the user to change the number to a bit higher.

For example:

Inside settings the rule is set to CN-002/2021. Inside the invoice creation page the user tries to change the rule to CN-001/2021, in this case we will throw an error because we won't like to allow this. If the user tries to change the rule to CN-003/2021, we allow it.

NOTE: The user can't change anything else, for example, CZ-003/2021 or CN-001/2022.

How can I check if the number is not lower?

This is the whole code:

def update(self, instance, validated_data):
    old_invoice_no = instance.number # Output CN-002/2021
    new_invoice_no = validated_data.get("number") # CN-001/2021

    if old_invoice_no != new_invoice_no:
        # .. Now the number has changed, how can we find the new number which was changed?
        # The number which has changed is 001 to 002

        # If the number format didn't match we throw an error
        return ValidationError("msg_error_malformed_invoice_number_pattern")

    # Everything seems good, we can update the instance
    validated_data = self._update_validated_data(validated_data)
    instance = super().update(instance, validated_data)
    self._update_customer(validated_data, instance)
    return instance

Upvotes: 0

Views: 71

Answers (2)

AvidJoe
AvidJoe

Reputation: 756

You can just check the number is higher or lower by doing str.split() a couple of times if the format of your invoice number doesn't change...

so your code can be modified as this in python3

def update(self, instance, validated_data):
    old_invoice_no = instance.number # Output CN-002/2021
    new_invoice_no = validated_data.get("number") # CN-001/2021

    if old_invoice_no != new_invoice_no:
        if(int((new_invoice_no.split('/')[0]).split('-')[1]) < int((old_invoice_no.split('/')[0]).split('-')[1]) or (new_invoice_no.split('/')[0]).split('-')[0] != (old_invoice_no.split('/')[0]).split('-')[0] or new_invoice_no.split('/')[1] != old_invoice_no.split('/')[1]):
          # New number invalid
          return ValidationError("msg_error_malformed_invoice_number_pattern")

    # Everything seems good, we can update the instance
    validated_data = self._update_validated_data(validated_data)
    instance = super().update(instance, validated_data)
    self._update_customer(validated_data, instance)
    return instance

if you are using earlier versions of python then you need to use string.atoi() instead of the int() cast inside the if. also the above only checks for what you specifically asked. This splits the string the same way multiple times but assuming the changes are not quite often. Should not be a bottleneck in your application.

Upvotes: 1

Kraigolas
Kraigolas

Reputation: 5560

Here is a working solution

import re

def validate(old, new):
    ost, omid, oend = re.split("[-/]", old)
    nst, nmid, nend = re.split("[-/]", new)
    if ost != nst or oend != nend:
        return False
    elif int(nmid) < int(omid):
        return False
    else:
        return True 

x = "CN-001/2021"
y = "CN-000/2021"
print(validate(x, y))
# False

x = "CN-001/2021"
y = "CN-002/2021"
print(validate(x, y))
# True

Here, we split the string at both - and /,so "CN-001/2021" becomes ["CN", "001", "2021"]. I called the variable names start, middle, and end here (ost for "old start", omid for "old mid", etc...). This code then returns False if the customer has entered an invalid entry, and True if their input is valid.

I would suggest validating the middle number further. For example, you could ensure that:

  1. nmid has the same number of characters as omid
  2. nmid is composed entirely of digits

You also may want to return a message from the validate function to report back what actually went wrong with the validation (eg. "It is illegal to change the start of the invoice number").

You might also consider correcting some changes. For example, if the user tries to modify "CN" to become "CZ" but the input is otherwise valid, you might want to just accept their changes, but replace nst with ost. Going further, I would suggest not even allowing the user to enter the start and end portions of the invoice number and instead, just ask them to enter a new number num and then creating the new invoice number as f"{ost}-{num}/{oend}"

Upvotes: 1

Related Questions