Repository: joke2k/faker

Issue:
Latvian ssn generator is not correct
* Faker version: 17.0.0
* OS: Windows


Latvian ssn generator generates ssn in format 'ddd-dd-dddd', which is not correct. 

### Steps to reproduce

```
from faker import Faker
fake = Faker('lv_LV')
fake.ssn()

```

### Expected behavior

It should be 12 characters long DDMMYY-CZZZQ e.g. 170334-19862

### Actual behavior

fake.ssn() for latvian locale outputs ssn in format  'ddd-dd-dddd'
'604-87-6475'
'824-21-9489'
'411-57-8156'
'938-77-8408'



Discussion:
`fake.ssn()` is not implemented/customized for the `lv_LV` locale at the moment (https://github.com/joke2k/faker/blob/master/faker/providers/ssn/lv_LV/__init__.py), thus falling back to the default implementation in https://github.com/joke2k/faker/blob/master/faker/providers/ssn/__init__.py

Feel free to submit a PR with an accompanying test which defines the correct `ssn_formats` list inside the provider or overwrite the corresponding `ssn()` method to return correctly formatted values.

Resolving patch:
diff --git a/faker/providers/ssn/lv_LV/__init__.py b/faker/providers/ssn/lv_LV/__init__.py
--- a/faker/providers/ssn/lv_LV/__init__.py
+++ b/faker/providers/ssn/lv_LV/__init__.py
@@ -1,7 +1,56 @@
-from .. import Provider as BaseProvider
+import datetime
 
+from .. import Provider as SsnProvider
+
+
+class Provider(SsnProvider):
+    def ssn(self, min_age: int = 0, max_age: int = 105) -> str:
+        """
+        Returns 11 character Latvian personal identity code (Personas kods).
+        This function assigns random age to person.
+
+        Personal code consists of eleven characters of the form DDMMYYCZZZQ, where
+        DDMMYY is the date of birth, C the century sign, ZZZ the individual
+        number and Q the control character (checksum). The number for the
+        century is either 0 (1800–1899), 1 (1900–1999), or 2 (2000–2099).
+        """
+
+        def _checksum(ssn_without_checksum):
+            weights = [1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
+            weighted_sum = sum(int(digit) * weight for digit, weight in zip(ssn_without_checksum, weights))
+            reminder = (1 - weighted_sum) % 11
+            if reminder == 10:
+                return 0
+            elif reminder < -1:
+                return reminder + 11
+            return reminder
+
+        age = datetime.timedelta(days=self.generator.random.randrange(min_age * 365, max_age * 365))
+        birthday = datetime.date.today() - age
+        ssn_date = "%02d%02d%s" % (
+            birthday.day,
+            birthday.month,
+            str(birthday.year)[-2:],
+        )
+        century = self._get_century_code(birthday.year)  # Century
+        suffix = self.generator.random.randrange(111, 999)
+        checksum = _checksum(f"{ssn_date}{century:01d}{suffix:03d}")
+        ssn = f"{ssn_date}-{century:01d}{suffix:03d}{checksum:01d}"
+        return ssn
+
+    @staticmethod
+    def _get_century_code(year: int) -> int:
+        """Returns the century code for a given year"""
+        if 2000 <= year < 3000:
+            code = 2
+        elif 1900 <= year < 2000:
+            code = 1
+        elif 1800 <= year < 1900:
+            code = 0
+        else:
+            raise ValueError("SSN do not support people born before the year 1800 or after the year 2999")
+        return code
 
-class Provider(BaseProvider):
     """
     A Faker provider for the Latvian VAT IDs
     """


Test patch:
diff --git a/tests/providers/test_ssn.py b/tests/providers/test_ssn.py
--- a/tests/providers/test_ssn.py
+++ b/tests/providers/test_ssn.py
@@ -26,6 +26,7 @@
 from faker.providers.ssn.fr_FR import calculate_checksum as fr_calculate_checksum
 from faker.providers.ssn.hr_HR import checksum as hr_checksum
 from faker.providers.ssn.it_IT import checksum as it_checksum
+from faker.providers.ssn.lv_LV import Provider as lv_Provider
 from faker.providers.ssn.no_NO import Provider as no_Provider
 from faker.providers.ssn.no_NO import checksum as no_checksum
 from faker.providers.ssn.pl_PL import calculate_month as pl_calculate_mouth
@@ -1236,3 +1237,37 @@ def setUp(self):
     def check_length(self):
         for sample in self.samples:
             assert len(sample) == 7
+
+
+class TestLvLV(unittest.TestCase):
+    num_sample_runs = 10
+
+    def setUp(self):
+        self.fake = Faker("lv_LV")
+        Faker.seed(0)
+        self.samples = [self.fake.ssn() for _ in range(self.num_sample_runs)]
+        self.provider = lv_Provider
+
+    def test_century_code(self):
+        assert self.provider._get_century_code(1900) == 1
+        assert self.provider._get_century_code(1999) == 1
+        assert self.provider._get_century_code(2000) == 2
+        assert self.provider._get_century_code(2999) == 2
+        assert self.provider._get_century_code(1800) == 0
+        assert self.provider._get_century_code(1899) == 0
+        with pytest.raises(ValueError):
+            self.provider._get_century_code(1799)
+        with pytest.raises(ValueError):
+            self.provider._get_century_code(3000)
+
+    def test_ssn_sanity(self):
+        for age in range(100):
+            self.fake.ssn(min_age=age, max_age=age + 1)
+
+    def check_length(self):
+        for sample in self.samples:
+            assert len(sample) == 12
+
+    def test_vat_id(self):
+        for _ in range(100):
+            assert re.search(r"^LV\d{11}$", self.fake.vat_id())