Completed
Push — main ( b7acb4...84e7fd )
by Jochen
03:57
created

delete_article_number_sequence()   A

Complexity

Conditions 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 6
nop 1
dl 0
loc 9
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
"""
2
byceps.services.shop.article.sequence_service
3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5
:Copyright: 2006-2020 Jochen Kupperschmidt
6
:License: Modified BSD, see LICENSE for details.
7
"""
8
9 1
from typing import List, Optional
10
11 1
from ....database import db
12
13 1
from ..sequence.models import NumberSequence as DbNumberSequence
14 1
from ..sequence.transfer.models import Purpose
15 1
from ..shop.transfer.models import ShopID
16
17 1
from .transfer.models import (
18
    ArticleNumber,
19
    ArticleNumberSequence,
20
    ArticleNumberSequenceID,
21
)
22
23
24 1
def create_article_number_sequence(
25
    shop_id: ShopID, prefix: str, *, value: Optional[int] = None
26
) -> ArticleNumberSequenceID:
27
    """Create an article number sequence."""
28 1
    sequence = DbNumberSequence(shop_id, Purpose.article, prefix, value=value)
29
30 1
    db.session.add(sequence)
31 1
    db.session.commit()
32
33 1
    return sequence.id
34
35
36 1
def delete_article_number_sequence(
37
    sequence_id: ArticleNumberSequenceID,
38
) -> None:
39
    """Delete the article number sequence."""
40 1
    db.session.query(DbNumberSequence) \
41
        .filter_by(id=sequence_id) \
42
        .delete()
43
44 1
    db.session.commit()
45
46
47 1
def find_article_number_sequence(
48
    sequence_id: ArticleNumberSequenceID,
49
) -> Optional[ArticleNumberSequence]:
50
    """Return the article number sequence, or `None` if the sequence ID
51
    is unknown or if the sequence's purpose is not article numbers.
52
    """
53
    sequence = DbNumberSequence.query \
54
        .filter_by(id=sequence_id) \
55
        .filter_by(_purpose=Purpose.article.name) \
56
        .one_or_none()
57
58
    if sequence is None:
59
        return None
60
61
    return _db_entity_to_article_number_sequence(sequence)
62
63
64 1
def find_article_number_sequences_for_shop(
65
    shop_id: ShopID,
66
) -> List[ArticleNumberSequence]:
67
    """Return the article number sequences defined for that shop."""
68
    sequences = DbNumberSequence.query \
69
        .filter_by(shop_id=shop_id) \
70
        .filter_by(_purpose=Purpose.article.name) \
71
        .all()
72
73
    return [
74
        _db_entity_to_article_number_sequence(sequence)
75
        for sequence in sequences
76
    ]
77
78
79 1
class ArticleNumberGenerationFailed(Exception):
80
    """Indicate that generating a prefixed, sequential article number
81
    has failed.
82
    """
83
84 1
    def __init__(self, message: str) -> None:
85
        self.message = message
86
87
88 1
def generate_article_number(
89
    sequence_id: ArticleNumberSequenceID,
90
) -> ArticleNumber:
91
    """Generate and reserve the next article number from this sequence."""
92 1
    sequence = DbNumberSequence.query \
93
        .filter_by(id=sequence_id) \
94
        .filter_by(_purpose=Purpose.article.name) \
95
        .with_for_update() \
96
        .one_or_none()
97
98 1
    if sequence is None:
99
        raise ArticleNumberGenerationFailed(
100
            f'No article number sequence found for ID "{sequence_id}".'
101
        )
102
103 1
    sequence.value = DbNumberSequence.value + 1
104 1
    db.session.commit()
105
106 1
    return ArticleNumber(f'{sequence.prefix}{sequence.value:05d}')
107
108
109 1
def _db_entity_to_article_number_sequence(
110
    sequence: DbNumberSequence,
111
) -> ArticleNumberSequence:
112
    return ArticleNumberSequence(
113
        sequence.id,
114
        sequence.shop_id,
115
        sequence.prefix,
116
        sequence.value,
117
    )
118