feat: approve priced carts at checkout so customers can pay at once
All checks were successful
Build and deploy / Validate source (push) Successful in 8s
Build and deploy / Integration suite on a real stack (push) Successful in 2m22s
Build and deploy / Secret scan and release gate (push) Successful in 6s
Build and deploy / Publish images (push) Successful in 1m34s
All checks were successful
Build and deploy / Validate source (push) Successful in 8s
Build and deploy / Integration suite on a real stack (push) Successful in 2m22s
Build and deploy / Secret scan and release gate (push) Successful in 6s
Build and deploy / Publish images (push) Successful in 1m34s
Every quote waited for an operator before it could be paid, so an order placed at night waited for the morning. A cart the Site priced is now approved when the quote is created, through the same server pricing the operator's approval uses (app/quote_review.py). Orders above QUOTE_AUTO_MAX_METRES (50 m) and items claiming a discount on art the Site could not analyse still wait for review; the Kanban shows which quotes were approved automatically and why the others wait. The grade is still computed in the browser (roadmap 3.2, 3.9), so the discount remains a customer-supplied value until the server computes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -94,6 +94,12 @@ def item_spec(mode, metres, grade, uid):
|
||||
'quality_status':'unverified' if grade==0 else 'ok',
|
||||
'quality_acknowledged':False}
|
||||
|
||||
def approved_quote(client, quote, items):
|
||||
"""The approval a customer pays against: automatic, or by the operator."""
|
||||
if quote['status']=='approved':
|
||||
return client.call('/quotes/'+quote['id'])['approved']
|
||||
return client.call('/operator/quotes/'+quote['id']+'/approve',{'items':items},operator=True)
|
||||
|
||||
def run():
|
||||
client=Client();other=Client()
|
||||
config=client.call('/session');other.call('/session')
|
||||
@@ -127,7 +133,8 @@ def run():
|
||||
except HTTPError as exc:assert exc.code==403
|
||||
print('PASS: multipart resume, incomplete rejection, immutable completion, ownership, private/downloaded bytes')
|
||||
|
||||
items=[item_spec(m,'2.75',90,uid) for m in ('file','avulsa','uvfile','uv')]
|
||||
# Above QUOTE_AUTO_MAX_METRES (50 m by default), so a person reviews it.
|
||||
items=[item_spec(m,'13',90,uid) for m in ('file','avulsa','uvfile','uv')]
|
||||
draft={'request_key':str(uuid4()),'customer':{'cnpj':'11222333000181','zap':'11999999999','mail':'local-smoke@example.test'},
|
||||
'items':items,'freight':{'service':'mock-standard','postal_code':'14400000'},
|
||||
'destination':{'recipient':'Local Smoke Ltda','street':'Rua de Teste','number':'100',
|
||||
@@ -146,6 +153,8 @@ def run():
|
||||
client.call('/quotes',{**draft,'items':[{**items[0],'metres':'1.00'}]},expected=422)
|
||||
client.call('/quotes',{**draft,'customer':{**draft['customer'],'cnpj':'11111111111111'}},expected=422)
|
||||
quote=client.call('/quotes',draft)
|
||||
assert quote['status']=='pending_review'
|
||||
assert client.call('/quotes/'+quote['id'])['review_reason']=='Pedido acima de 50 m'
|
||||
assert client.call('/quotes',draft)['id']==quote['id']
|
||||
client.call('/quotes',{**draft,'freight':{'service':'pickup'},'destination':None},expected=409)
|
||||
qid=quote['id']
|
||||
@@ -159,9 +168,26 @@ def run():
|
||||
corrected=[{**items[0],'metres':'1.01','grade':0},*items[1:]]
|
||||
approved=client.call('/operator/quotes/'+qid+'/approve',{'items':corrected},operator=True)
|
||||
assert approved['items'][0]['total_cents']==2189
|
||||
assert approved['total_cents']==2189+6972+19572+23492+int(os.environ.get('MOCK_FREIGHT_CENTS','1500'))
|
||||
assert approved['total_cents']==2189+32370+90870+109070+int(os.environ.get('MOCK_FREIGHT_CENTS','1500'))
|
||||
assert approved['destination']=={**draft['destination'],'complement':''}
|
||||
client.call('/operator/quotes/'+qid+'/approve',{'items':items},operator=True,expected=409)
|
||||
assert not client.call('/quotes/'+qid)['auto_approved']
|
||||
# A cart the Site priced is approved at once and can be paid straight away,
|
||||
# at the server's own prices; no operator can then change it.
|
||||
small={**draft,'request_key':str(uuid4()),'items':[item_spec('avulsa','2.75',90,uid)]}
|
||||
auto=client.call('/quotes',small)
|
||||
assert auto['status']=='approved'
|
||||
seen=client.call('/quotes/'+auto['id'])
|
||||
assert seen['auto_approved'] and seen['review_reason'] is None
|
||||
assert seen['approved']['total_cents']==6972+int(os.environ.get('MOCK_FREIGHT_CENTS','1500'))
|
||||
assert client.call('/quotes',small)['status']=='approved'
|
||||
client.call('/operator/quotes/'+auto['id']+'/approve',{'items':small['items']},operator=True,expected=409)
|
||||
# The Site grades only art it analysed; a discount on unanalysed art waits for a person.
|
||||
claimed={**item_spec('avulsa','2.75',0,uid),'grade':90}
|
||||
held=client.call('/quotes',{**small,'request_key':str(uuid4()),'items':[claimed]})
|
||||
assert held['status']=='pending_review'
|
||||
assert client.call('/quotes/'+held['id'])['review_reason']=='Nota informada sem análise da arte'
|
||||
print('PASS: priced carts are approved at checkout; large or inconsistent ones wait for review')
|
||||
client.call('/orders/dev-paid',{'quote_id':qid,'total_cents':1},expected=422)
|
||||
other.call('/orders/dev-paid',{'quote_id':qid},expected=404)
|
||||
# Concurrent retries must produce precisely one payment/order/outbox pair.
|
||||
|
||||
Reference in New Issue
Block a user