django-barobill 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_barobill/__init__.py +2 -0
- django_barobill/admin.py +3 -0
- django_barobill/apps.py +6 -0
- django_barobill/choices.py +39 -0
- django_barobill/errors.py +67 -0
- django_barobill/migrations/0001_initial.py +78 -0
- django_barobill/migrations/__init__.py +0 -0
- django_barobill/models.py +387 -0
- django_barobill/parsers.py +29 -0
- django_barobill/tests.py +48 -0
- django_barobill/utils.py +9 -0
- django_barobill/views.py +3 -0
- django_barobill-0.1.0.dist-info/LICENSE +0 -0
- django_barobill-0.1.0.dist-info/METADATA +31 -0
- django_barobill-0.1.0.dist-info/RECORD +17 -0
- django_barobill-0.1.0.dist-info/WHEEL +5 -0
- django_barobill-0.1.0.dist-info/top_level.txt +1 -0
django_barobill/admin.py
ADDED
django_barobill/apps.py
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
from django.db import models
|
2
|
+
|
3
|
+
class BankAccountCollectCycle(models.TextChoices):
|
4
|
+
MINUTE10 = 'MINUTE10', '10분'
|
5
|
+
MINUTE30 = 'MINUTE30', '30분'
|
6
|
+
HOUR1 = 'HOUR1', '1시간'
|
7
|
+
HOUR4 = 'HOUR4', '4시간'
|
8
|
+
DAY1 = 'DAY1', '1일'
|
9
|
+
|
10
|
+
class BankAccountBank(models.TextChoices):
|
11
|
+
KB = 'KB', '국민은행'
|
12
|
+
SHINHAN = 'SHINHAN', '신한은행'
|
13
|
+
NH = 'NH', '농협은행'
|
14
|
+
HANA = 'HANA', '하나은행'
|
15
|
+
SC = 'SC', '제일은행'
|
16
|
+
WOORI = 'WOORI', '우리은행'
|
17
|
+
IBK = 'IBK', '기업은행'
|
18
|
+
KDB = 'KDB', '산업은행'
|
19
|
+
KFCC = 'KFCC', '새마을금고'
|
20
|
+
CITI = 'CITI', '씨티은행'
|
21
|
+
SUHYUP = 'SUHYUP', '수협은행'
|
22
|
+
CU = 'CU', '신협은행'
|
23
|
+
EPOST = 'EPOST', '우체국'
|
24
|
+
KJBANK = 'KJBANK', '광주은행'
|
25
|
+
JBBANK = 'JBBANK', '전북은행'
|
26
|
+
DGB = 'DGB', '대구은행'
|
27
|
+
BUSANBANK = 'BUSANBANK', '부산은행'
|
28
|
+
KNBANK = 'KNBANK', '경남은행'
|
29
|
+
EJEJUBANK = 'EJEJUBANK', '제주은행'
|
30
|
+
KBANK = 'KBANK', '케이뱅크'
|
31
|
+
|
32
|
+
class BankAccountAccountType(models.TextChoices):
|
33
|
+
C = 'C', '법인계좌'
|
34
|
+
P = 'P', '개인계좌'
|
35
|
+
|
36
|
+
class BankAccountTransactionDirection(models.TextChoices):
|
37
|
+
Deposit = 'D', '입금'
|
38
|
+
Withdraw = 'W', '출금'
|
39
|
+
ETC = 'E', '기타'
|
@@ -0,0 +1,67 @@
|
|
1
|
+
error_dict = {
|
2
|
+
# 커스텀
|
3
|
+
901: "해지된 계좌 입니다",
|
4
|
+
902: "해지되지 않은 계좌 입니다",
|
5
|
+
# 기본 오류코드
|
6
|
+
-10000: "알 수 없는 오류 발생. API 호출 중 서버오류가 발생한 경우입니다. 바로빌로 문의바랍니다.",
|
7
|
+
-10003: "연동서비스가 점검 중입니다.",
|
8
|
+
-10004: "해당 기능은 더 이상 사용되지 않습니다.",
|
9
|
+
-10007: "해당 기능을 사용할 수 없습니다.",
|
10
|
+
-10005: "최대 100건까지만 사용하실 수 있습니다.",
|
11
|
+
-10006: "최대 1000건까지만 사용하실 수 있습니다.",
|
12
|
+
-10008: "날짜형식이 잘못되었습니다.",
|
13
|
+
-10148: "조회 기간이 잘못되었습니다.",
|
14
|
+
-40001: "파일을 찾을 수 없습니다.",
|
15
|
+
-40002: "빈 파일입니다(0byte).",
|
16
|
+
# 스크래핑 공통
|
17
|
+
-51001: "서비스가 신청되지 않았습니다.",
|
18
|
+
-51002: "이미 신청되어 있습니다.",
|
19
|
+
-51003: "이미 해지되었습니다.",
|
20
|
+
-51004: "해지 상태가 아닙니다.",
|
21
|
+
-51005: "해지 당월에만 해지 취소할 수 있습니다.",
|
22
|
+
-51006: "해지 당월에는 재신청 할 수 없습니다. 해지 취소로 진행해주세요.",
|
23
|
+
-51007: "테스트베드는 최대 2개까지만 등록할 수 있습니다.",
|
24
|
+
-51008: "이미 수집중입니다.",
|
25
|
+
# 연동정보 관련
|
26
|
+
-10002: "해당 인증키를 찾을 수 없습니다.",
|
27
|
+
-10001: "해당 인증키와 연결된 연계사가 아닙니다.",
|
28
|
+
-24005: "사업자번호와 아이디가 맞지 않습니다.",
|
29
|
+
-50001: "계좌를 찾을 수 없습니다.",
|
30
|
+
-50002: "계좌를 조회할 권한이 없습니다.",
|
31
|
+
-50004: "일일 즉시조회 횟수(10회)를 초과하였습니다.",
|
32
|
+
-50211: "수집주기가 잘못 입력되었습니다.",
|
33
|
+
-50212: "은행 코드가 잘못 입력되었습니다.",
|
34
|
+
-50213: "계좌유형이 잘못 입력되었습니다.",
|
35
|
+
-50214: "계좌번호가 잘못 입력되었습니다.",
|
36
|
+
-50215: "계좌 비밀번호가 잘못 입력되었습니다.",
|
37
|
+
-50216: "계좌 비밀번호를 입력하지 않아야하는 은행입니다.",
|
38
|
+
-50217: "빠른조회 아이디가 잘못 입력되었습니다.",
|
39
|
+
-50218: "빠른조회 아이디를 입력하지 않아야하는 은행입니다.",
|
40
|
+
-50219: "빠른조회 비밀번호가 잘못 입력되었습니다.",
|
41
|
+
-50220: "빠른조회 비밀번호를 입력하지 않아야하는 은행입니다.",
|
42
|
+
-50221: "사업자번호 또는 주민번호(앞6자리)가 잘못 입력되었습니다.",
|
43
|
+
-50222: "사업자번호 또는 주민번호(앞6자리)를 입력하지 않아야하는 은행입니다.",
|
44
|
+
-50223: "유효한 계좌정보가 아닙니다. 계좌정보를 확인해 주시기 바랍니다.",
|
45
|
+
-50224: "계좌정보 검증에 실패하였습니다. 잠시 후 다시 시도해 주시기 바랍니다.",
|
46
|
+
-50225: "이미 등록된 계좌번호입니다.",
|
47
|
+
-50226: "간편조회(빠른조회)서비스에 등록되지 않은 계좌입니다.",
|
48
|
+
-50251: "입출금내역 키(TransRefKey)가 잘못 입력되었습니다.",
|
49
|
+
-50252: "계좌 입출금내역을 찾을 수 없습니다."
|
50
|
+
}
|
51
|
+
|
52
|
+
|
53
|
+
class BarobillBaseError(Exception):
|
54
|
+
def __init__(self, code, message=None):
|
55
|
+
self.code = int(code)
|
56
|
+
self.message = message or error_dict.get(self.code, f"알 수 없는 오류 코드 : {self.code}")
|
57
|
+
|
58
|
+
def __str__(self):
|
59
|
+
return self.message
|
60
|
+
|
61
|
+
|
62
|
+
class BarobillAPIError(BarobillBaseError):
|
63
|
+
pass
|
64
|
+
|
65
|
+
|
66
|
+
class BarobillError(BarobillBaseError):
|
67
|
+
pass
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# Generated by Django 5.1.4 on 2025-01-16 01:16
|
2
|
+
|
3
|
+
import django.db.models.deletion
|
4
|
+
from django.db import migrations, models
|
5
|
+
|
6
|
+
|
7
|
+
class Migration(migrations.Migration):
|
8
|
+
|
9
|
+
initial = True
|
10
|
+
|
11
|
+
dependencies = [
|
12
|
+
]
|
13
|
+
|
14
|
+
operations = [
|
15
|
+
migrations.CreateModel(
|
16
|
+
name='Partner',
|
17
|
+
fields=[
|
18
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
19
|
+
('name', models.CharField(max_length=255, unique=True, verbose_name='파트너사명')),
|
20
|
+
('brn', models.CharField(max_length=10, unique=True, verbose_name='사업자등록번호')),
|
21
|
+
('api_key', models.CharField(max_length=36, verbose_name='인증키')),
|
22
|
+
('userid', models.CharField(max_length=256, verbose_name='파트너사 사용자 아이디')),
|
23
|
+
('dev', models.BooleanField(default=False, verbose_name='테스트모드')),
|
24
|
+
],
|
25
|
+
options={
|
26
|
+
'verbose_name': '파트너',
|
27
|
+
'verbose_name_plural': '파트너',
|
28
|
+
},
|
29
|
+
),
|
30
|
+
migrations.CreateModel(
|
31
|
+
name='BankAccount',
|
32
|
+
fields=[
|
33
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
34
|
+
('collect_cycle', models.CharField(choices=[('MINUTE10', '10분'), ('MINUTE30', '30분'), ('HOUR1', '1시간'), ('HOUR4', '4시간'), ('DAY1', '1일')], max_length=10, verbose_name='수집주기')),
|
35
|
+
('bank', models.CharField(choices=[('KB', '국민은행'), ('SHINHAN', '신한은행'), ('NH', '농협은행'), ('HANA', '하나은행'), ('SC', '제일은행'), ('WOORI', '우리은행'), ('IBK', '기업은행'), ('KDB', '산업은행'), ('KFCC', '새마을금고'), ('CITI', '씨티은행'), ('SUHYUP', '수협은행'), ('CU', '신협은행'), ('EPOST', '우체국'), ('KJBANK', '광주은행'), ('JBBANK', '전북은행'), ('DGB', '대구은행'), ('BUSANBANK', '부산은행'), ('KNBANK', '경남은행'), ('EJEJUBANK', '제주은행'), ('KBANK', '케이뱅크')], max_length=10, verbose_name='은행')),
|
36
|
+
('account_type', models.CharField(choices=[('C', '법인계좌'), ('P', '개인계좌')], max_length=1, verbose_name='계좌유형')),
|
37
|
+
('account_no', models.CharField(max_length=50, verbose_name='계좌번호')),
|
38
|
+
('alias', models.CharField(default=None, max_length=50, null=True, verbose_name='별칭')),
|
39
|
+
('usage', models.CharField(default=None, max_length=50, null=True, verbose_name='용도')),
|
40
|
+
('is_stop', models.BooleanField(default=False, verbose_name='해지')),
|
41
|
+
('stop_date', models.DateField(default=None, null=True, verbose_name='해지일')),
|
42
|
+
('partner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='django_barobill.partner', verbose_name='파트너')),
|
43
|
+
],
|
44
|
+
options={
|
45
|
+
'verbose_name': '계좌',
|
46
|
+
'verbose_name_plural': '계좌',
|
47
|
+
},
|
48
|
+
),
|
49
|
+
migrations.CreateModel(
|
50
|
+
name='BankAccountTransaction',
|
51
|
+
fields=[
|
52
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
53
|
+
('trans_direction', models.CharField(choices=[('D', '입금'), ('W', '출금'), ('E', '기타')], max_length=1)),
|
54
|
+
('deposit', models.BigIntegerField(default=None, null=True)),
|
55
|
+
('withdraw', models.BigIntegerField(default=None, null=True)),
|
56
|
+
('balance', models.BigIntegerField(default=0)),
|
57
|
+
('trans_dt', models.DateTimeField()),
|
58
|
+
('trans_type', models.CharField(default=None, max_length=50, null=True, verbose_name='입출금구분')),
|
59
|
+
('trans_office', models.CharField(default=None, max_length=50, null=True, verbose_name='입출금취급점')),
|
60
|
+
('trans_remark', models.CharField(default=None, max_length=50, null=True, verbose_name='입출금비고')),
|
61
|
+
('mgt_remark_1', models.CharField(default=None, max_length=50, null=True, verbose_name='비고1')),
|
62
|
+
('trans_ref_key', models.CharField(max_length=24, verbose_name='입출금내역 키')),
|
63
|
+
('memo', models.CharField(default=None, max_length=100, null=True, verbose_name='입출금내역 키')),
|
64
|
+
('meta', models.JSONField(default=None, null=True, verbose_name='추가 정보')),
|
65
|
+
('bank_account', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='django_barobill.bankaccount')),
|
66
|
+
],
|
67
|
+
options={
|
68
|
+
'verbose_name': '입출금 기록',
|
69
|
+
'verbose_name_plural': '입출금 기록',
|
70
|
+
'ordering': ['-trans_dt', 'bank_account'],
|
71
|
+
'unique_together': {('bank_account', 'trans_ref_key')},
|
72
|
+
},
|
73
|
+
),
|
74
|
+
migrations.AddConstraint(
|
75
|
+
model_name='bankaccount',
|
76
|
+
constraint=models.UniqueConstraint(fields=('partner', 'bank', 'account_no'), name='unique_partner_bank_account'),
|
77
|
+
),
|
78
|
+
]
|
File without changes
|
@@ -0,0 +1,387 @@
|
|
1
|
+
import datetime
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
from django.db import models
|
5
|
+
from django.utils import timezone
|
6
|
+
from zeep import Client
|
7
|
+
|
8
|
+
from django_barobill.choices import BankAccountCollectCycle, BankAccountBank, BankAccountAccountType, BankAccountTransactionDirection
|
9
|
+
from django_barobill.errors import BarobillAPIError, BarobillError
|
10
|
+
from django_barobill.parsers import bank_account_log_parser
|
11
|
+
|
12
|
+
|
13
|
+
class BankHelper:
|
14
|
+
def __init__(self, partner):
|
15
|
+
self.partner = partner
|
16
|
+
self.client = self._get_client()
|
17
|
+
|
18
|
+
def _get_client(self):
|
19
|
+
if self.partner.dev:
|
20
|
+
endpoint = "https://testws.baroservice.com/BANKACCOUNT.asmx?wsdl"
|
21
|
+
else:
|
22
|
+
endpoint = "https://ws.baroservice.com/BANKACCOUNT.asmx?wsdl"
|
23
|
+
return Client(endpoint)
|
24
|
+
|
25
|
+
def get_bank_account_management_url(self):
|
26
|
+
return self.client.service.GetBankAccountManagementURL(
|
27
|
+
CERTKEY=self.partner.api_key,
|
28
|
+
CorpNum=self.partner.brn,
|
29
|
+
ID=self.partner.userid,
|
30
|
+
PWD=''
|
31
|
+
)
|
32
|
+
|
33
|
+
def get_bank_account_list(self, avail_only: bool):
|
34
|
+
response = self.client.service.GetBankAccountEx(
|
35
|
+
CERTKEY=self.partner.api_key,
|
36
|
+
CorpNum=self.partner.brn,
|
37
|
+
AvailOnly=1 if avail_only is True else 0,
|
38
|
+
)
|
39
|
+
if response is None:
|
40
|
+
return []
|
41
|
+
return response
|
42
|
+
|
43
|
+
def register_bank_account(
|
44
|
+
self,
|
45
|
+
alias: str,
|
46
|
+
collect_cycle: BankAccountCollectCycle,
|
47
|
+
bank: BankAccountBank,
|
48
|
+
account_type: BankAccountAccountType,
|
49
|
+
account_no: str,
|
50
|
+
password: str,
|
51
|
+
usage: Optional[str] = None,
|
52
|
+
web_id: Optional[str] = None,
|
53
|
+
web_pwd: Optional[str] = None,
|
54
|
+
identity_num: Optional[str] = None
|
55
|
+
):
|
56
|
+
# need test
|
57
|
+
result = self.client.service.RegisterBankAccount(
|
58
|
+
CERTKEY=self.partner.api_key,
|
59
|
+
CorpNum=self.partner.brn,
|
60
|
+
CollectCycle=collect_cycle,
|
61
|
+
Bank=bank,
|
62
|
+
BankAccountType=account_type,
|
63
|
+
BankAccountNum=account_no,
|
64
|
+
BankAccountPwd=password,
|
65
|
+
WebId=web_id,
|
66
|
+
WebPwd=web_pwd,
|
67
|
+
IdentityNum=identity_num,
|
68
|
+
Alias=alias,
|
69
|
+
Usage=usage,
|
70
|
+
)
|
71
|
+
if result != 1:
|
72
|
+
raise BarobillAPIError(result)
|
73
|
+
account, created = BankAccount.objects.update_or_create(
|
74
|
+
partner=self.partner, account_no=account_no, defaults=dict(
|
75
|
+
collect_cycle=collect_cycle, bank=bank, account_type=account_type, alias=alias, usage=usage, is_delete=False
|
76
|
+
)
|
77
|
+
)
|
78
|
+
return account
|
79
|
+
|
80
|
+
def update_bank_accounts(self):
|
81
|
+
account_list = self.get_bank_account_list(avail_only=False)
|
82
|
+
account_no_list = [acc.BankAccountNum for acc in account_list]
|
83
|
+
self.partner.bankaccount_set.filter(
|
84
|
+
account_no__in=account_no_list
|
85
|
+
).update(is_stop=False, stop_date=None, )
|
86
|
+
self.partner.bankaccount_set.exclude(
|
87
|
+
account_no__in=account_no_list
|
88
|
+
).update(is_stop=True, stop_date=timezone.localdate())
|
89
|
+
|
90
|
+
|
91
|
+
class Partner(models.Model):
|
92
|
+
class Meta:
|
93
|
+
verbose_name = '파트너'
|
94
|
+
verbose_name_plural = verbose_name
|
95
|
+
|
96
|
+
name = models.CharField(max_length=255, unique=True, verbose_name='파트너사명')
|
97
|
+
brn = models.CharField(max_length=10, unique=True, verbose_name='사업자등록번호')
|
98
|
+
api_key = models.CharField(max_length=36, verbose_name='인증키')
|
99
|
+
userid = models.CharField(max_length=256, verbose_name='파트너사 사용자 아이디')
|
100
|
+
dev = models.BooleanField(default=False, null=False, verbose_name='테스트모드')
|
101
|
+
|
102
|
+
def __init__(self, *args, **kwargs):
|
103
|
+
super().__init__(*args, **kwargs)
|
104
|
+
self.bank = BankHelper(self)
|
105
|
+
|
106
|
+
|
107
|
+
class BankAccount(models.Model):
|
108
|
+
class Meta:
|
109
|
+
verbose_name = '계좌'
|
110
|
+
verbose_name_plural = verbose_name
|
111
|
+
constraints = [
|
112
|
+
models.UniqueConstraint(fields=['partner', 'bank', 'account_no'], name='unique_partner_bank_account'),
|
113
|
+
]
|
114
|
+
|
115
|
+
partner = models.ForeignKey(Partner, null=False, blank=False, verbose_name='파트너', on_delete=models.PROTECT)
|
116
|
+
collect_cycle = models.CharField(
|
117
|
+
max_length=10, null=False, blank=False, choices=BankAccountCollectCycle.choices, verbose_name='수집주기'
|
118
|
+
)
|
119
|
+
bank = models.CharField(
|
120
|
+
max_length=10, null=False, blank=False, choices=BankAccountBank.choices, verbose_name='은행'
|
121
|
+
)
|
122
|
+
account_type = models.CharField(
|
123
|
+
max_length=1, null=False, blank=False, choices=BankAccountAccountType.choices, verbose_name='계좌유형'
|
124
|
+
)
|
125
|
+
account_no = models.CharField(
|
126
|
+
max_length=50, null=False, blank=False, verbose_name='계좌번호'
|
127
|
+
)
|
128
|
+
alias = models.CharField(max_length=50, null=True, blank=False, default=None, verbose_name='별칭')
|
129
|
+
usage = models.CharField(max_length=50, null=True, blank=False, default=None, verbose_name='용도')
|
130
|
+
is_stop = models.BooleanField(default=False, null=False, verbose_name='해지')
|
131
|
+
stop_date = models.DateField(null=True, blank=False, default=None, verbose_name='해지일')
|
132
|
+
|
133
|
+
def update_bank_account(
|
134
|
+
self,
|
135
|
+
password: Optional[str] = None,
|
136
|
+
web_id: Optional[str] = None,
|
137
|
+
web_pwd: Optional[str] = None,
|
138
|
+
identity_num: Optional[str] = None,
|
139
|
+
alias: Optional[str] = None,
|
140
|
+
usage: Optional[str] = None
|
141
|
+
):
|
142
|
+
request_data = dict(
|
143
|
+
CERTKEY=self.partner.api_key,
|
144
|
+
CorpNum=self.partner.brn,
|
145
|
+
BankAccountNum=self.account_no
|
146
|
+
)
|
147
|
+
if password:
|
148
|
+
request_data['BankAccountPwd'] = password
|
149
|
+
if web_id:
|
150
|
+
request_data['WebId'] = web_id
|
151
|
+
if web_pwd:
|
152
|
+
request_data['WebPwd'] = web_pwd
|
153
|
+
if identity_num:
|
154
|
+
request_data['IdentityNum'] = identity_num
|
155
|
+
if alias:
|
156
|
+
request_data['Alias'] = alias
|
157
|
+
if usage:
|
158
|
+
request_data['Usage'] = usage
|
159
|
+
result = self.partner.bank.client.service.UpdateBankAccount(**request_data)
|
160
|
+
if result != 1:
|
161
|
+
raise BarobillAPIError(result)
|
162
|
+
save_fields = []
|
163
|
+
if self.alias != alias and alias is not None:
|
164
|
+
self.alias = alias
|
165
|
+
save_fields.append('alias')
|
166
|
+
if self.usage != usage and usage is not None:
|
167
|
+
self.usage = usage
|
168
|
+
save_fields.append('usage')
|
169
|
+
if len(save_fields) != 0:
|
170
|
+
self.save(update_fields=save_fields)
|
171
|
+
|
172
|
+
def stop_bank_account(self):
|
173
|
+
if self.is_stop is True:
|
174
|
+
raise BarobillError(901)
|
175
|
+
request_data = dict(
|
176
|
+
CERTKEY=self.partner.api_key,
|
177
|
+
CorpNum=self.partner.brn,
|
178
|
+
BankAccountNum=self.account_no
|
179
|
+
)
|
180
|
+
result = self.partner.bank.client.service.StopBankAccount(**request_data)
|
181
|
+
if result != 1:
|
182
|
+
raise BarobillAPIError(result)
|
183
|
+
self.is_stop = True
|
184
|
+
self.stop_date = timezone.localdate()
|
185
|
+
self.save(update_fields=['is_stop', 'stop_date'])
|
186
|
+
|
187
|
+
def cancel_stop_bank(self):
|
188
|
+
if self.is_stop is False:
|
189
|
+
raise BarobillError(902)
|
190
|
+
request_data = dict(
|
191
|
+
CERTKEY=self.partner.api_key,
|
192
|
+
CorpNum=self.partner.brn,
|
193
|
+
BankAccountNum=self.account_no
|
194
|
+
)
|
195
|
+
result = self.partner.bank.client.service.CancelStopBankAccount(**request_data)
|
196
|
+
if result != 1:
|
197
|
+
raise BarobillAPIError(result)
|
198
|
+
self.is_stop = False
|
199
|
+
self.stop_date = None
|
200
|
+
self.save(update_fields=['is_stop', 'stop_date'])
|
201
|
+
|
202
|
+
def re_register_bank_account(self):
|
203
|
+
if self.is_stop is False:
|
204
|
+
raise BarobillError(902)
|
205
|
+
request_data = dict(
|
206
|
+
CERTKEY=self.partner.api_key,
|
207
|
+
CorpNum=self.partner.brn,
|
208
|
+
BankAccountNum=self.account_no
|
209
|
+
)
|
210
|
+
result = self.partner.bank.client.service.ReRegistBankAccount(**request_data)
|
211
|
+
if result != 1:
|
212
|
+
raise BarobillAPIError(result)
|
213
|
+
self.is_stop = False
|
214
|
+
self.stop_date = None
|
215
|
+
self.save(update_fields=['is_stop', 'stop_date'])
|
216
|
+
|
217
|
+
def __update_log(self, log_list):
|
218
|
+
"""
|
219
|
+
BankAccountLogEx2 의 list 값을 거래 기록에 등록한다.
|
220
|
+
:param log_list: [BankAccountLogEx2]
|
221
|
+
:return:None
|
222
|
+
"""
|
223
|
+
parsed_log_list = map(bank_account_log_parser, log_list)
|
224
|
+
transactions = BankAccountTransaction.objects.bulk_create(
|
225
|
+
[BankAccountTransaction(bank_account=self, **parsed_log) for parsed_log in parsed_log_list],
|
226
|
+
ignore_conflicts=True
|
227
|
+
)
|
228
|
+
|
229
|
+
def update_date_log(self, date_string: str):
|
230
|
+
"""
|
231
|
+
지정한 날짜의 로그를 불러와 데이터베이스에 저장한다.
|
232
|
+
:param date_string: YYYYMMDD 형태의 한국 날짜 string
|
233
|
+
:return: None
|
234
|
+
"""
|
235
|
+
page = 1
|
236
|
+
log_list = []
|
237
|
+
while True:
|
238
|
+
result = self.get_daily_log(date_string, page)
|
239
|
+
log_list += [] if result.BankAccountLogList is None else result.BankAccountLogList.BankAccountLogEx2
|
240
|
+
if result.MaxPageNum == page:
|
241
|
+
break
|
242
|
+
page += 1
|
243
|
+
self.__update_log(log_list)
|
244
|
+
|
245
|
+
def update_today_log(self):
|
246
|
+
"""
|
247
|
+
오늘 거래 내역을 불러오고, 데이터베이스에 저장한다.
|
248
|
+
:return: None
|
249
|
+
"""
|
250
|
+
date_string = timezone.localdate().strftime('%Y%m%d')
|
251
|
+
self.update_date_log(date_string)
|
252
|
+
|
253
|
+
def update_yesterday_log(self):
|
254
|
+
"""
|
255
|
+
어제의 거래 내역을 불러오고, 데이터베이스에 저장한다.
|
256
|
+
:return: None
|
257
|
+
"""
|
258
|
+
date_string = (timezone.localdate() - datetime.timedelta(days=1)).strftime('%Y%m%d')
|
259
|
+
self.update_date_log(date_string)
|
260
|
+
|
261
|
+
def get_log(
|
262
|
+
self,
|
263
|
+
start_date: str,
|
264
|
+
end_date: str,
|
265
|
+
page: int,
|
266
|
+
direction: int = 1,
|
267
|
+
per_page: int = 100,
|
268
|
+
order: int = 1
|
269
|
+
):
|
270
|
+
"""
|
271
|
+
주어진 기간에 대한 거래 내역을 불러온다.
|
272
|
+
https://dev.barobill.co.kr/docs/references/%EA%B3%84%EC%A2%8C%EC%A1%B0%ED%9A%8C-API#GetPeriodBankAccountLogEx2
|
273
|
+
:param start_date: 한국시간 YYYYMMDD 형태의 조회 시작일 string.
|
274
|
+
:param end_date: 한국시간 YYYYMMDD 형태의 조회 종료일 string.
|
275
|
+
:param page: 조회할 페이지 수
|
276
|
+
:param direction: 거래 유형. 1 전체, 2 입금, 3출금
|
277
|
+
:param per_page: 페이지 당 결과 표시 수(최대 100개)
|
278
|
+
:param order: 거래일시 정렬 순서. 1 오름차순, 2. 내림차순
|
279
|
+
:return: barobill API 결과를 그대로 반환한다.(https://dev.barobill.co.kr/docs/references/%EA%B3%84%EC%A2%8C%EC%A1%B0%ED%9A%8C-API#PagedBankAccountLogEx2)
|
280
|
+
"""
|
281
|
+
request_data = dict(
|
282
|
+
CERTKEY=self.partner.api_key,
|
283
|
+
CorpNum=self.partner.brn,
|
284
|
+
ID=self.partner.userid,
|
285
|
+
BankAccountNum=self.account_no,
|
286
|
+
StartDate=start_date,
|
287
|
+
EndDate=end_date,
|
288
|
+
TransDirection=direction,
|
289
|
+
CountPerPage=per_page,
|
290
|
+
CurrentPage=page,
|
291
|
+
OrderDirection=order # 1 오름차순, 2 내림차순
|
292
|
+
)
|
293
|
+
result = self.partner.bank.client.service.GetPeriodBankAccountLogEx2(**request_data)
|
294
|
+
if result.CurrentPage <= 0:
|
295
|
+
raise BarobillAPIError(result.CurrentPage)
|
296
|
+
return result
|
297
|
+
|
298
|
+
def get_daily_log(
|
299
|
+
self,
|
300
|
+
base_date: str,
|
301
|
+
page: int,
|
302
|
+
direction: int = 1,
|
303
|
+
per_page: int = 100,
|
304
|
+
order: int = 1
|
305
|
+
):
|
306
|
+
"""
|
307
|
+
주어진 일자의 거래 내역을 불러온다.
|
308
|
+
https://dev.barobill.co.kr/docs/references/%EA%B3%84%EC%A2%8C%EC%A1%B0%ED%9A%8C-API#GetDailyBankAccountLogEx2
|
309
|
+
:param base_date: 한국시간 YYYYMMDD 형태의 조회일 string.
|
310
|
+
:param page: 조회할 페이지 수
|
311
|
+
:param direction: 거래 유형. 1 전체, 2 입금, 3출금
|
312
|
+
:param per_page: 페이지 당 결과 표시 수(최대 100개)
|
313
|
+
:param order: 거래일시 정렬 순서. 1 오름차순, 2. 내림차순
|
314
|
+
:return: barobill API 결과를 그대로 반환한다.(https://dev.barobill.co.kr/docs/references/%EA%B3%84%EC%A2%8C%EC%A1%B0%ED%9A%8C-API#PagedBankAccountLogEx2)
|
315
|
+
"""
|
316
|
+
request_data = dict(
|
317
|
+
CERTKEY=self.partner.api_key,
|
318
|
+
CorpNum=self.partner.brn,
|
319
|
+
ID=self.partner.userid,
|
320
|
+
BankAccountNum=self.account_no,
|
321
|
+
BaseDate=base_date,
|
322
|
+
TransDirection=direction,
|
323
|
+
CountPerPage=per_page,
|
324
|
+
CurrentPage=page,
|
325
|
+
OrderDirection=order # 1 오름차순, 2 내림차순
|
326
|
+
)
|
327
|
+
result = self.partner.bank.client.service.GetDailyBankAccountLogEx2(**request_data)
|
328
|
+
if result.CurrentPage <= 0:
|
329
|
+
raise BarobillAPIError(result.CurrentPage)
|
330
|
+
return result
|
331
|
+
|
332
|
+
|
333
|
+
def get_monthly_log(
|
334
|
+
self,
|
335
|
+
base_month: str,
|
336
|
+
page: int,
|
337
|
+
direction: int = 1,
|
338
|
+
per_page: int = 100,
|
339
|
+
order: int = 1
|
340
|
+
):
|
341
|
+
"""
|
342
|
+
주어진 일자의 거래 내역을 불러온다.
|
343
|
+
https://dev.barobill.co.kr/docs/references/%EA%B3%84%EC%A2%8C%EC%A1%B0%ED%9A%8C-API#GetMonthlyBankAccountLogEx2
|
344
|
+
:param base_month: 한국시간 YYYYMM 형태의 조회월 string.
|
345
|
+
:param page: 조회할 페이지 수
|
346
|
+
:param direction: 거래 유형. 1 전체, 2 입금, 3출금
|
347
|
+
:param per_page: 페이지 당 결과 표시 수(최대 100개)
|
348
|
+
:param order: 거래일시 정렬 순서. 1 오름차순, 2. 내림차순
|
349
|
+
:return: barobill API 결과를 그대로 반환한다.(https://dev.barobill.co.kr/docs/references/%EA%B3%84%EC%A2%8C%EC%A1%B0%ED%9A%8C-API#PagedBankAccountLogEx2)
|
350
|
+
"""
|
351
|
+
request_data = dict(
|
352
|
+
CERTKEY=self.partner.api_key,
|
353
|
+
CorpNum=self.partner.brn,
|
354
|
+
ID=self.partner.userid,
|
355
|
+
BankAccountNum=self.account_no,
|
356
|
+
BaseMonth=base_month,
|
357
|
+
TransDirection=direction,
|
358
|
+
CountPerPage=per_page,
|
359
|
+
CurrentPage=page,
|
360
|
+
OrderDirection=order # 1 오름차순, 2 내림차순
|
361
|
+
)
|
362
|
+
result = self.partner.bank.client.service.GetDailyBankAccountLogEx2(**request_data)
|
363
|
+
if result.CurrentPage <= 0:
|
364
|
+
raise BarobillAPIError(result.CurrentPage)
|
365
|
+
return result
|
366
|
+
|
367
|
+
|
368
|
+
class BankAccountTransaction(models.Model):
|
369
|
+
class Meta:
|
370
|
+
verbose_name = '입출금 기록'
|
371
|
+
verbose_name_plural = verbose_name
|
372
|
+
unique_together = ('bank_account', 'trans_ref_key')
|
373
|
+
ordering = ['-trans_dt', 'bank_account']
|
374
|
+
|
375
|
+
bank_account = models.ForeignKey(BankAccount, null=False, blank=False, on_delete=models.PROTECT)
|
376
|
+
trans_direction = models.CharField(max_length=1, null=False, blank=False, choices=BankAccountTransactionDirection.choices)
|
377
|
+
deposit = models.BigIntegerField(null=True, blank=False, default=None)
|
378
|
+
withdraw = models.BigIntegerField(null=True, blank=False, default=None)
|
379
|
+
balance = models.BigIntegerField(null=False, blank=False, default=0)
|
380
|
+
trans_dt = models.DateTimeField(null=False, blank=False)
|
381
|
+
trans_type = models.CharField(max_length=50, null=True, blank=False, default=None, verbose_name='입출금구분')
|
382
|
+
trans_office = models.CharField(max_length=50, null=True, blank=False, default=None, verbose_name='입출금취급점')
|
383
|
+
trans_remark = models.CharField(max_length=50, null=True, blank=False, default=None, verbose_name='입출금비고')
|
384
|
+
mgt_remark_1 = models.CharField(max_length=50, null=True, blank=False, default=None, verbose_name='비고1')
|
385
|
+
trans_ref_key = models.CharField(max_length=24, null=False, blank=False, verbose_name='입출금내역 키')
|
386
|
+
memo = models.CharField(max_length=100, null=True, blank=False, default=None, verbose_name='입출금내역 키')
|
387
|
+
meta = models.JSONField(null=True, blank=False, default=None, verbose_name='추가 정보')
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import datetime
|
2
|
+
|
3
|
+
from django_barobill.utils import KST
|
4
|
+
|
5
|
+
|
6
|
+
def bank_account_log_parser(item):
|
7
|
+
"""
|
8
|
+
BankAccountLogEx2를 데이터베이스에 적재할 수 있는 형태로 파싱한다.
|
9
|
+
:param item: BankAccountLogEx2를
|
10
|
+
:return: BankAccountTransaction
|
11
|
+
"""
|
12
|
+
from django_barobill.choices import BankAccountTransactionDirection
|
13
|
+
return dict(
|
14
|
+
trans_direction={
|
15
|
+
"입금": BankAccountTransactionDirection.Deposit,
|
16
|
+
"출금": BankAccountTransactionDirection.Withdraw,
|
17
|
+
"기타": BankAccountTransactionDirection.ETC,
|
18
|
+
}.get(item.TransDirection),
|
19
|
+
deposit=int(item.Deposit),
|
20
|
+
withdraw=int(item.Withdraw),
|
21
|
+
balance=int(item.Balance),
|
22
|
+
trans_dt=KST.localize(datetime.datetime.strptime(item.TransDT, "%Y%m%d%H%M%S")),
|
23
|
+
trans_type=item.TransType,
|
24
|
+
trans_office=item.TransOffice,
|
25
|
+
trans_remark=item.TransRemark,
|
26
|
+
mgt_remark_1=item.MgtRemark1,
|
27
|
+
trans_ref_key=item.TransRefKey,
|
28
|
+
memo=item.Memo,
|
29
|
+
)
|
django_barobill/tests.py
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
import datetime
|
2
|
+
|
3
|
+
from django.test import TestCase
|
4
|
+
from django.utils import timezone
|
5
|
+
|
6
|
+
from django_barobill.choices import BankAccountBank, BankAccountCollectCycle, BankAccountAccountType
|
7
|
+
from django_barobill.models import Partner, BankAccount
|
8
|
+
from django_barobill.parsers import bank_account_log_parser
|
9
|
+
|
10
|
+
|
11
|
+
# Create your tests here.
|
12
|
+
class TestBankAccount(TestCase):
|
13
|
+
def setUp(self):
|
14
|
+
self.partner = Partner.objects.create(
|
15
|
+
name='지구하다ERP',
|
16
|
+
brn='2978601887',
|
17
|
+
api_key='0830C49B-71EA-44E0-81E0-DAAD883B3FC9',
|
18
|
+
userid='cuhong',
|
19
|
+
dev=True
|
20
|
+
)
|
21
|
+
self.bank = BankAccountBank.HANA
|
22
|
+
self.account_no = '26091002164604'
|
23
|
+
self.bank_account = BankAccount.objects.create(
|
24
|
+
partner=self.partner,
|
25
|
+
collect_cycle=BankAccountCollectCycle.MINUTE10,
|
26
|
+
bank=self.bank,
|
27
|
+
account_type=BankAccountAccountType.C,
|
28
|
+
account_no=self.account_no,
|
29
|
+
)
|
30
|
+
|
31
|
+
def test_get_yesterday_log(self):
|
32
|
+
date_string = (timezone.localdate() - datetime.timedelta(days=0)).strftime('%Y%m%d')
|
33
|
+
log_list = []
|
34
|
+
page = 1
|
35
|
+
while True:
|
36
|
+
result = self.bank_account.get_daily_log(date_string, page)
|
37
|
+
log_list += [] if result.BankAccountLogList is None else result.BankAccountLogList.BankAccountLogEx2
|
38
|
+
if result.MaxPageNum == page:
|
39
|
+
break
|
40
|
+
page += 1
|
41
|
+
for log in log_list:
|
42
|
+
print(bank_account_log_parser(log))
|
43
|
+
|
44
|
+
def test_update_today_log(self):
|
45
|
+
self.bank_account.update_today_log()
|
46
|
+
|
47
|
+
ba = BankAccount.objects.first()
|
48
|
+
ba.update_yesterday_log()
|
django_barobill/utils.py
ADDED
django_barobill/views.py
ADDED
File without changes
|
@@ -0,0 +1,31 @@
|
|
1
|
+
Metadata-Version: 2.2
|
2
|
+
Name: django-barobill
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: A reusable Django app for integration with BaroBill services.
|
5
|
+
Home-page: https://github.com/cuhong/django-barobill
|
6
|
+
Author: cuhong
|
7
|
+
Author-email: hongcoilhouse@gmail.com
|
8
|
+
License: MIT License
|
9
|
+
Classifier: Environment :: Web Environment
|
10
|
+
Classifier: Framework :: Django
|
11
|
+
Classifier: Framework :: Django :: 4.0
|
12
|
+
Classifier: Intended Audience :: Developers
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
14
|
+
Classifier: Programming Language :: Python
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
17
|
+
Requires-Python: >=3.8
|
18
|
+
Description-Content-Type: text/markdown
|
19
|
+
License-File: LICENSE
|
20
|
+
Requires-Dist: django>=4.0
|
21
|
+
Requires-Dist: requests==2.32.3
|
22
|
+
Requires-Dist: zeep==4.3.1
|
23
|
+
Dynamic: author
|
24
|
+
Dynamic: author-email
|
25
|
+
Dynamic: classifier
|
26
|
+
Dynamic: description-content-type
|
27
|
+
Dynamic: home-page
|
28
|
+
Dynamic: license
|
29
|
+
Dynamic: requires-dist
|
30
|
+
Dynamic: requires-python
|
31
|
+
Dynamic: summary
|
@@ -0,0 +1,17 @@
|
|
1
|
+
django_barobill/__init__.py,sha256=6APRtokGxem92Eirujfn1lSSABIc2gFsVvfwDIOEf7c,52
|
2
|
+
django_barobill/admin.py,sha256=suMo4x8I3JBxAFBVIdE-5qnqZ6JAZV0FESABHOSc-vg,63
|
3
|
+
django_barobill/apps.py,sha256=VUIGjvRJJKmOM0WaBbqoU5QULCvC0YGKhKIMFYGpXxI,161
|
4
|
+
django_barobill/choices.py,sha256=qkh5baFFCycTOKcTiU2DSu2iDJTiIQyhFgQ8uNSWyDo,1236
|
5
|
+
django_barobill/errors.py,sha256=lKEEEKzAdwAueQOLhoSLe-B9VLT-S7hqUCrkfpkRXp8,3555
|
6
|
+
django_barobill/models.py,sha256=Utls8E_l_dh8Yb8O8HibuaUQara6KakHqQk_RfmZ5K0,16134
|
7
|
+
django_barobill/parsers.py,sha256=boBB9ePTRJrOmoEql23VQk_TDcaWqbEwJhxtW9Gx2Yc,1027
|
8
|
+
django_barobill/tests.py,sha256=iG0hzKegA86ls4QxLY_p6sgZ0fkb_CD0HyzY-aO28ic,1661
|
9
|
+
django_barobill/utils.py,sha256=eKSI-QgM8WTHA1vUukbZnVKEhiMcX7wzxLPcJ_6VKVE,200
|
10
|
+
django_barobill/views.py,sha256=xc1IQHrsij7j33TUbo-_oewy3vs03pw_etpBWaMYJl0,63
|
11
|
+
django_barobill/migrations/0001_initial.py,sha256=Uh8Nx0mOJQjXnPhzXmT7mrQx5zTbE8bNQSJJXTqHNpU,5176
|
12
|
+
django_barobill/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
|
+
django_barobill-0.1.0.dist-info/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
|
+
django_barobill-0.1.0.dist-info/METADATA,sha256=kyC3pz_IHWpZX05M7_5JhfpWEkQmxi5Sg6AyLCnZx7s,996
|
15
|
+
django_barobill-0.1.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
16
|
+
django_barobill-0.1.0.dist-info/top_level.txt,sha256=RmcbZUqTKXVI1jksKTaeennFkcSemFHwgxSMPqE7YgU,16
|
17
|
+
django_barobill-0.1.0.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
django_barobill
|