# Բանկոմատ
Վերջնաժամկետ՝ **01/12/2023 23:59**
## Ներածություն
Պրոյեկտի շրջանակում ուզում ենք պատրաստել բանկոմատ։
Բանկոմատի հիմնական հնարավորությունները ընդգրկելու են՝
- Կոդերով թղթադրամներ
- Հաղորդակցություն արտաքին բանկի հետ
- (Ցանկուոյան դեպքում) Կոդավորված հաղորդակցություն բանկի հետ
## Բանկոմատի Ընդհանուր Աշխատանք
Բանկոմատը անընդհատ աշխատում է այս սխեմայով՝
- Սպասում է քարտի համարի մութքի
- Հարցնում է քարտի PIN կոդ
1. **WITHDRAW** ընտրելուց սպասարկում է գումարի ելքը
2. **DEPOSIT** ընտրելուց սպասարկում է գումարի մութքը
3. **CHECK BALANCE** ընտրելուց սպասարկում է քարտի բալանսի ստուգումը
4. **CHANGE PIN** ընտրելուց սպասարկում է PIN կոդի փոխումը
Հրահանգների սպասարկամ մանրամասները հաջորթող մասերում են։
### Բանկ
Բանկի հետ կարելի է աշխատել [bank.py](https://gev.serveo.net/tent/project/bank.py)֊ի կողմից տրամադրած մեթոդների միջոցով։
Ցանկության դեպքում կարելի թեստավորման նպատակով որոշ փոփխություններ կատարել սեփական `bank.py`֊ին, բայց հարկավոր է հիշել, որ լուծումը ուղարկելուց փոփոխումներով `bank.py` չի ընդունվելու։
### Քարտեր
Քարտերը **string** են, որոնք ունեն "card0000" ֆորմատը, 0000֊ի տեղը քարտի համարը։
Քարտերը իրենց հետ կապված նաև ունեն 4֊նիշանոց PIN-կոդ որպես և դրամային բալանս։
PIN-կոդը որպես **string** և բալանսը որպես **int**։
Քարտերի օրինակները գտնվում են `bank.py`֊ի `db` բառարանում։
### Թղթադրամներ
Թղթադրամները ունեն կոդ, որը բաղկացաց է երկու տառից և չորս թվից։ Երկու տառը արտահայտում են թղթադրամի արժեքը `ha`, `er`, `hi`, `ta`, `qs` համապատասխանաբար `1000`, `2000`, `5000`, `10000`, `20000`։
Թղթադրամների կոդի օրինակ՝ `ha4851` համապատասխանում է կոդով `4851` հազար դրամանոցին։
### Քարտի ստուգում
Բանկոմատը տպում է "INSERT CARD: " և մութքից ստանում է քարտ։
Եթե քարտի տեղը մութքագրվել է "EXIT", ավարտում է աշխատանքը։
Հակառակ դեպքում ստուգում է քարտը `bank.card_exists(card)` մեթոդով։
Եթե քարտը գոյություն չունի կամ այլ մութքի տվյալների սխալներ կան, ապա տպում է "UNKNOWN CARD" և շարունակում իր աշխատանքը։
Ճիշտ քարտ ստանալուց հետո, հարցնում է PIN-կոդ։
PIN-կոդը կարելի է ստուգել `bank.valid_pin(card, otp, safe=True)` մեթոդով, որը ունի երկու ռեժիմ, անապահով և ապահով։
- Ապահով ռեժիմը կոդավորում է PIN-կոդը և ստանում է մեկ անգամվա օգտագործման կոդ (One-Time Password) կամ ուղակի otp (Կոդավորման Բաժին)
- Անապահով ռեժիմը չի կոդավորում PIN-կոդը և ուղակի otp֊ի տեղը մի անգամից տալիս է PIN-կոդը, և `safe`֊ին տալիս է `False` արժեքը
Եթե PIN-կոդը սխալ է, տպում է "WRONG PIN" և վերադառնում իր աշխատանքին։
Հակառակ դեպքում տպում է համարակալած մենյուն։
```
1 WITHDRAW
2 DEPOSIT
3 CHECK BALANCE
4 CHANGE PIN
CHOICE:
```
1-4 Մութքագրված թվիծ կախված սպասարկում է ընտրված օպեացիան։
Այլ արժեք ստանալու դեպքում, ուղակի նորից է հարցնում։
----
## 1 WITHDRAW
Նախ տպելով
```
ONLY MULTIPLES OF 1000
```
տեղեկացնում է, որ պատրաստ է տրամադրել միայն 1000֊ին բազմապատիկ արժեքներ։
Ապա տպում է "ENTER AMOUNT: " և ակնկալում է 1000֊ին բազմապատիկ թիվ։
```
ENTER AMOUNT: 18000
```
Եթե մութքագրված արժեքը սխալ ֆորմատ ունի, նորից է հարցնում։
Եթե մութքագրված արժեքը 1000֊ին բազմապատիկ չէ, տպում է
```
INVALID AMOUNT
```
Հետո, ստուգում է արդյոք քարտին այդքան գումար կա `bank.can_withdraw(card, otp, amount, safe=True)` մեթոդով։
Եթե ոչ տպում է
```
LACK OF FUNDS
```
Հետո, ստուգում է արդյոք իր ներսում եղած թղթադրամներով կարղ է ստանալ պահանջվող գումարը, եթե ոչ ապա տպում է
```
REQUESTED AMOUNT UNAVAILABLE
```
Վերջապես, `bank.withdraw(card, otp, amount, safe=True)` մեթոդով հաստատում է բալանսի մարումը բանկում, և տպում է ընտրված թղթադրամների կոդերը։ Օրինակ՝
```
COLLECT YOUR CASH: ta0124 hi3123 er3214 ha4851
```
Բանկոմատը պետք է փորձի տալ հնարավորինս մեծ թղթադրամներով։
**Բանկոմատը աշխատանքը սկսելուց չպետք է ունենա իր ներսում ոչ մի թղթադրամ**։ Ավտոմատ թեստերում ենթադրվելու է, որ բանկոմատը սկզբում դատարկ է։
Բանկոմատը ընտրում է թղթադրամներ կախված մութքագրման հերթականությանունից։ Այսինքն մութգարլեուց դրամարկղի նման թղթադրամները իրար վրա է հավաքում ըստ կատեգորիայի (Stack֊ի նման Last-In-First-Out)։ Հետևաբար, այն դեպքերում երբ, որ պետք է ընտրել օրինակ երկու հազար դրամից մեկը, կնտրվի ավելի ուշ մութքագրվածը
## 2 DEPOSIT
Նախ տպելով
```
ONLY 1000, 2000, 5000, 10000, 20000
```
տեղեկացնում է, որ պատրաստ է ընդունել միայն նշված թղթադրամները։
Ապա տպում է "ENTER BILLS: " և ակնկալում մի տողում գրված, բացատով առանձնացված թղթադրամների կոդեր։
Օրինակ՝
```
ENTER BILLS: ta0124 hi3123
```
Հետո, `bank.valid_bill(bill)` մեթոդի միջոցով ստուգում է թղթադրամի կոդի առկայությունը։
Եթե մութքագրած թղթադրամներից որևէ մեկը սխալ ֆորմատ ունի, կամ չի ճանաչվում բանկի կողմից ապա տպում է "INVALID BILLS: " և այդպիսի միայն թղթադրամների կոդերը բացատով առանձնացված։ Տպելուց հետո վերադառնում է իր աշխատանքին։ Օրինակ՝
```
INVALID BILLS: hazar_dram
```
Եթե խնդիր չկա, իր հիշողության մեջ ավելացնում է այդ թղթադրամները, բանկում իրենց գումարով ավելացնում է balance֊ը `bank.deposit(card, otp, amount, safe=True)` մեթոդով և տպում է։
```
SUCCESS. ENTERED AMOUNT XXXX AMD
```
Հակառակ դեպում եթե որևէ այլ խնդիր կա, ապա չի ընդունում թղթադրամները, բալանսը չի ավելացնում, և տպում է
```
DEPOSIT ERROR
```
Որից հետո վերադառնում իր աշխատանքին։
## 3 CHECK BALANCE
Օգտագործելով `bank.get_balance(card, otp, safe=True)` մեթոդը, տպում է քարտի բալանսը
```
BALANCE: XXXX AMD
```
ֆորմատով։
## 4 CHANGE PIN
Հարցնում է նոր PIN֊կոդ նախ "NEW PIN: " տպելով։
Եթե նոր PIN֊կոդը չի համապատասխանում PIN-կոդի 4 նշանոց ֆորմատին, ուղակի նորից է հարցնում։
Ճիշտ ֆորմատով PIN-կոդ ստանալուց հետո օգտագործելով `bank.change_pin(card, otp, new_pin, safe=True)` մեթոդը, փորձում է փոխել PIN֊ը։
Եթե հաջողվում է փոխել, ապա տպում է
```
PIN CHANGED
```
և վերադառնում իր աշխատանքին։
Իսկ եթե ոչ, ապա ոչ մի բան չի տպում, և ուղակի վերադառնում իր աշխատանքին։
## Կոդավորում
Ապահովության համար բանկոմատը չի պահում PIN֊կոդը, այլ պահում է hash(pin)֊ից ստացված հաշը։ Իսկ Բանկին այդ հաշը ուղարկելուց առաջ [1; 1000000] պատահական թիվ է ընտրում գումարում հաշին և նոր ուղարկում այդ արժեքը բանկին, որպես otp (One-Time Password)։
Բանկը և բանկոմատը նույն "CODETENT" Seed-ով են միացնում իրենց random օբյեկտները։
Ընդ, որում բանկոմատը ամեն հրահանգ կատարելուց որտեղ otp է օգտագործվում, պետք է սկզբնական հաշին նոր հաջորդական թիվ գումարի։
Բանկոմատը կարող է ասել բանկին, որպեսզի նա seed֊ով զրոյացնի իր random օբյեկտը։ Օգտվելով `bank.resync()` մեթոդից։
Այդպես կարելի անել բանկոմատի աշխատանքի ամենա սկզբում։
OTP Օրինակ: Ենթադրենք մենք նոր ենք միացրել բանկոմատը և մութգարվել է "4242" PIN-կոդը, մենք մի անգամից վերցնում ենք նրա հաշը, ենթադրենք -4067239946432026864։ Ապա կանչենք valid_pin մեթոդը, որի համար
```
otp = pin_hash + random.randint(1,1000000)
```
Քանի, որ random֊ը "CODETENT" Seed֊ով զրոյացվել էր, ապա կստանանք՝
```
otp = -4067239946432026864 + 577862 = -4067239946431449002
```
Հաջորդ otp արժեքը, օրինակ get_balance() կանչելու համար արդեն կլինի նույն ձև, բայց կստացվի ուրիշ արժեք
```
otp = -4067239946432026864 + 719988 = -4067239946431306876
```
## Գնահատում
Ընդհանուր պրոյեկտի համար կարելի է հավաքել **100** միավոր։
Ընդ որում, ցանկության դեպքում կարելի է բաց թողել որոշ մասեր։
Ամեն մաս գնահատվելու է ըստ այս միավորների ցուցակի՝
(**25** միավոր): Բանկոմատի ընդհանուր ճիշտ աշխատանք։
(**25** միավոր): **WITHDRAW**
(**20** միավոր): **DEPOSIT**
(**10** միավոր): **CHECK BALANCE**
(**10** միավոր): **CHANGE PIN**
(**10** միավոր): Կոդավորված հաղորդակցություն բանկի հետ
- Միավորներրը լինելու են ավտոմատ թեսթերից կախված, դրա համար **չի կարելի** տպել բնութագրված սահմաններից դուրս արժեքներ, ընդ որում, տերմինալը մաքրող նշաններ և կոդեր։
Ավտոմատ թեստերը լինելու են [tests.zip](https://gev.serveo.net/tent/project/tests.zip) թեստավորող ծրագրով։ Այն պատկանում է մի քանի պարզ թեսթեր, և վերջնական գնահատելուց ունենալու է հավելյալ թեստեր։ (Թեստավորող ծրագիրը ենթադրում է, որ բանկոմատը սկզբում դատարկ է)։