본문 바로가기
Programming/Django

[게르만 민족 프로젝트]Django로 정산하기

by IN.0 2020. 6. 11.
728x90
반응형

지난 포스팅에서 카카오페이로 결제한 건을 OrderList에 기록하였다.

지난 포스팅 ↓

https://in0-pro.tistory.com/27

 

[게르만 민족 프로젝트]Django로 카카오페이 API 사용하기2

프로젝트를 어느 정도 진행한 후 지난번에 작성한 카카오페이 API 사용 후기와 달라진 점이 많아서 2탄을 만들었다 지난번 게시글 ↓↓↓↓↓↓↓↓ https://in0-pro.tistory.com/16 [게르만 민족 프로젝�

in0-pro.tistory.com

 

이제 OrderList를 사용하여 사장님 페이지에서 날짜를 선택하면 해당 기간의 매출을 보여주는 페이지를 만들어보자.

 

path('<int:store_pk>/pocket/', views.pocket, name="pocket"),

접근 url은 이러하다.

 

그리고 views.py에서 기본 설정은 아래와 같다.

def pocket(request, store_pk):
    store = get_object_or_404(Store, pk=store_pk)
    user = request.user
    if is_manager(user, store):
        context = {
            'store': store,
        }
        return render(request, 'ceos/pocket.html', context)
    else:
        return redirect('main:index')

접근하는 유저가 해당 가게의 매니저가 맞는지 확인하고, 유효성 검사를 통과한 유저만 해당 페이지에 접속할 수 있다.

context로 가게 정보를 넘겨준다.

 

ceos/pocket.html은 아래와 같이 구성되어있다.

{% extends 'base.html' %}
{% load bootstrap_pagination %}

{% block content %}
<h1>{{ store.store_name }}</h1>

<div class="dropdown d-inline-block">
  <a class="btn btn-secondary dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
    선택
  </a>
  <div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
    <a class="dropdown-item" id="getDay">일별</a>
    <a class="dropdown-item" id="getMonth">월별</a>
    <a class="dropdown-item" id="getYear">년도별</a>
    <a class="dropdown-item" id="getAll">전체</a>
  </div>
</div>

<div class="d-inline-block ml-3" id="subOptionDiv">
</div>

<div class="d-inline-block">합계 : <div id="sum" class="d-inline-block"></div>원</div>

<table class="table table-striped">
  <thead>
    <tr>
      <th scope="col">#</th>
      <th scope="col">주문 시각</th>
      <th scope="col">주문 금액</th>
    </tr>
  </thead>
  <tbody id="myTable">
  </tbody>
</table>

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script>
  axios.defaults.xsrfCookieName = 'csrftoken'
  axios.defaults.xsrfHeaderName = "X-CSRFTOKEN"
  let today = new Date();

  getDay();
  document.getElementById('selectedYear').value = today.getFullYear()
  document.getElementById('selectedMonth').value = today.getMonth()+1
  document.getElementById('selectedDay').value = today.getDate()
  search();

  var getDayClick = document.getElementById('getDay');
  getDayClick.addEventListener('click', function(){
      getDay();
  });
  var getMonthClick = document.getElementById('getMonth');
  getMonthClick.addEventListener('click', function(){
      getMonth();
  });
  var getYearClick = document.getElementById('getYear');
  getYearClick.addEventListener('click', function(){
      getYear();
  });
  var getAllClick = document.getElementById('getAll');
  getAllClick.addEventListener('click', function(){
      getAll();
  });

  function createYear(parent) {
    var sel = document.createElement("select");
    sel.id = "selectedYear";
    for(var i=today.getFullYear(); i > 2018; i--) {
      var op = document.createElement("option");
      op.value = i;
      op.text = i;
      sel.appendChild(op);
    }
    parent.appendChild(sel);
  };

  function createMonth(parent) {
    var sel = document.createElement("select");
    sel.id = "selectedMonth";
    for(var i=1; i < 13; i++) {
      var op = document.createElement("option");
      op.value = i;
      op.text = i;
      sel.appendChild(op);
    }
    parent.appendChild(sel);
  };

  function createDay(parent) {
    var sel = document.createElement("select");
    sel.id = "selectedDay";
    for(var i=1; i < 32; i++) {
      var op = document.createElement("option");
      op.value = i;
      op.text = i;
      sel.appendChild(op);
    }
    parent.appendChild(sel);
  };

  function createButton(parent) {
    var bt = document.createElement("button");
    bt.innerHTML = "조회"
    bt.className = "btn btn-info"
    bt.onclick = function(){
      search();
    };
    parent.appendChild(bt);
  };

  function getDay() {
    var di = document.getElementById('subOptionDiv');
    while ( di.hasChildNodes() ) {
      di.removeChild( di.firstChild );
    };
    document.getElementById('dropdownMenuLink').text = "일별";

    createYear(di);
    createMonth(di);
    createDay(di);
    createButton(di);


  };

  function getMonth() {
    var di = document.getElementById('subOptionDiv');
    while ( di.hasChildNodes() ) {
      di.removeChild( di.firstChild );
    };
    document.getElementById('dropdownMenuLink').text = "월별";

    createYear(di);
    createMonth(di);
    createButton(di);
  };

  function getYear() {
    var di = document.getElementById('subOptionDiv');
    while ( di.hasChildNodes() ) {
      di.removeChild( di.firstChild );
    };
    document.getElementById('dropdownMenuLink').text = "년도별";

    createYear(di);
    createButton(di);
  };

  function getAll() {
    var di = document.getElementById('subOptionDiv');
    while ( di.hasChildNodes() ) {
      di.removeChild( di.firstChild );
    };
    document.getElementById('dropdownMenuLink').text = "전체";

    createButton(di);

  };

  function numberWithCommas(x) {
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  }

  function createTable(parent, cnt, time, price) {
    var tr = document.createElement("tr");
    var td = document.createElement("td");
    td.innerText = cnt;
    tr.appendChild(td);
    var td = document.createElement("td");
    td.innerText = time;
    tr.appendChild(td);
    var td = document.createElement("td");
    td.innerText = numberWithCommas(price)+'원';
    tr.appendChild(td);

    parent.appendChild(tr);
  };

  function search() {
    var body = document.getElementById('myTable');
    while ( body.hasChildNodes() ) {
      body.removeChild( body.firstChild );
    };
    try {
      var year = document.getElementById('selectedYear').value;
    } catch (e) {
      var year = '2020';
    }
    try {
      var month = document.getElementById('selectedMonth').value;
    } catch (e) {
      var month = '1';
    }
    try {
      var day = document.getElementById('selectedDay').value;
    } catch (e) {
      var day = '1';
    }
    var standard = document.getElementById('dropdownMenuLink').text

    axios.post('/ceos/pocket/calculator/', { params: {
        store_pk: {{ store.pk }},
        year: year,
        month: month,
        day: day,
        standard: standard,
    }})
     .then(res => {
        var cnt = 1
        var sumPrice = 0
        res['data']['orders'].forEach(
          order => {
            createTable(body, cnt++, order['order_time'], order['order_price']);
            sumPrice += order['order_price'];
          }
        );
        document.getElementById('sum').innerHTML = numberWithCommas(sumPrice);
      })
     .catch(err => console.error(err))
  }



</script>

{% endblock %}

다소 길지만 javascript function별로 살펴보자.

65번째 줄 creatYear는 년도를 선택하는 dropdown을 생성하는 함수이다.

40번째 줄에 정의한 today를 활용하여 현재 년도부터 2019년까지 나타낸다.

이와 유사한 createMonth, createDay 함수도 각각 월, 일을 선택하는 dropdown을 생성해준다.

createButton은 버튼을 생성해주는데, 사용자가 원하는 날짜를 고르고 이 버튼을 클릭하면 search 함수로 넘어가게 된다.

 

111번째 getDay는 사용자가 년, 월, 일을 모두 선택할 수 있도록 createYear, createMonth, createDay 모두 실행한다.

getMonth는 년, 월만 지정하면 그 달의 1일부터 31일(또는 30일)을 모두 조회한다. 따라서 createYear, createMonth만 필요하다.

getYear는 년도만 지정하면 해당 년도의 1월 1일부터 12월 31일까지 모두 조회한다. 따라서 createYear만 있으면 된다.

getAll은 모든 OrderList 목록을 가져오기 때문에 날짜 설정을 필요하지 않다.

 

search 함수는 사용자가 dropdown으로 지정한 값을 year, month, day로 받는다. 지정한 값이 없을 경우 year는 2020, month는 1, day는 1로 기본 설정한다.

그리고 axios로 views.py에서 정의한 calculator를 연결하여 json으로 통신한다.

def calculator(request):
    data = json.loads(request.body.decode('utf-8'))
    store = get_object_or_404(Store, pk=data['params']['store_pk'])
    if is_manager(request.user, store):
        standard = data['params']['standard']
        year = int(data['params']['year'])
        month = int(data['params']['month'])
        day = int(data['params']['day'])
        if standard == '일별':
            start_date = datetime.datetime(year, month, day, 0, 0, 0)
            end_date = datetime.datetime(year, month, day, 23, 59, 59)
        elif standard == '월별':
            start_date = datetime.datetime(year, month, 1, 0, 0, 0)
            if month in [1, 3, 5, 7, 8, 10, 12]:
                end_date = datetime.datetime(year, month, 31, 23, 59, 59)
            else:
                end_date = datetime.datetime(year, month, 30, 23, 59, 59)
        elif standard == '년도별':
            start_date = datetime.datetime(year, 1, 1, 0, 0, 0)
            end_date = datetime.datetime(year, 12, 31, 23, 59, 59)
        else:
            today = datetime.datetime.today()
            start_date = datetime.datetime(2019, 1, 1, 0, 0, 0)
            end_date = datetime.datetime(today.year, today.month, today.day, 23, 59, 59)

        orders = list(store.orderlist_set.all().filter(order_time__range=(start_date, end_date)).values())
        context = {
            'message': 'OK',
            'orders': orders,
        }
        return JsonResponse(context)
    else:
        context = {
            'message': 'ERROR',
        }
        return JsonResponse(context)

standard 값에 따라 start_date와 end_date를 정의하고, 해당 가게의 OrderList를 날짜로 필터링하여 리스트로 만들어준다.

여기에서 not json serializable 이슈가 있었는데, 해결 방법은 아래 포스팅에 있다. ↓

https://in0-pro.tistory.com/25

 

not json serializable 오류 해결 방법

json을 사용하는데 typeerror: object of type queryset is not json serializable 오류가 떴다. json 모듈이 해당 오브젝트를 제대로 인식하지 못하는 문제로 보였다. 구글에 오류를 검색해보니 list로 감싸서 해..

in0-pro.tistory.com

 

다시 search 함수로 돌아가서, 응답받은 데이터를 res로 정의하고

forEach로 각 데이터를 이용하여 createTable을 실행하며 sum에 매출 합계를 누적한다.

그리고 금액은 3자리에서 콤마(,)로 끊어주는 numberWithCommas 함수를 이용한다.

 

완성본 (팀원 이름은 가렸다)

 

마지막으로 지금까지 정의한 것들을 HTML 태그와 연결시켜주면 끝이다.

결제 날짜가 아닌, 메뉴별로 집계를 내고싶은 욕심도 있었는데 시간이 애매하여 여기서 마무리 지었다.

원래 javascript에는 별로 관심이 없었는데, 이 기능들을 구현하면서 어려운 점도 많았고 재미도 있어 흥미가 생겼다.

아직 부족한 점이 많은데 (미래 날짜 선택을 막는 기능이나 2월 28, 29일 처리 등등..)

다음에는 조금 더 완성도를 높일 수 있을 것 같다.

728x90
반응형

댓글