Site icon Bệ Phóng Việt

Flask 6: Tạo hóa đơn cho khách hàng

Advertisements

Đây là Bài 6 trong chuối các bài hướng dẫn sử dụng Python Flask cơ bản. Quay về Bài 5: Tạo giao diện để xem dữ liệu khách hàng

Vậy là ở các bài trước, bạn đã học cách quản lý cơ sở dữ liệu cách khách hàng. Bạn đã biết cách thêm, liệt kê và xem thông tin khách hàng. Bạn cũng đã học cách tạo bluepring, route, và template cho các chức năng.

Trong bài này, chúng ta sẽ tiếp tục với chức năng tạo hóa đơn cho 1 khách hàng. Bạn sẽ học các thao tác phức tạp hơn trong template, và sử dụng câu lệnh JOIN của SQL.

Trước khi bắt đầu, chúng ta hãy cùng xem cấu trúc của bảng hóa đơn (invoice) đã được tạo từ đầu khóa học:

CREATE TABLE invoice (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  customer_id INTEGER NOT NULL,
  invoice_date DATE,
  description TEXT NOT NULL,
  sub_total FLOAT,
  discount FLOAT,
  tax FLOAT,
  FOREIGN KEY (customer_id) REFERENCES customer (id)
);

Bảng này có 1 khóa phụ customer_id, được kế nối với khóa chính “id” của bảng customer.

Tạo blueprint cho invoice và đăng ký trong app

Tại thư mục gốc flask-customer/mycustomer, bạn hãy tạo file blueprint invoice.py với đoạn code sau:

from werkzeug.exceptions import abort

# import các mod cần thiết cho 1 blueprint cơ bản:
from flask import (
    Blueprint, flash, g, redirect, render_template, request, session, url_for
)

# import hàm get_db từ module db mà chúng ta đã viết ở bài trước. Hàm này phục vụ cho việc truy vấn CSDL.
from mycustomer.db import get_db
from . import customer

# tạo blueprint cho module invoice
bp = Blueprint('invoice', __name__, url_prefix='/invoice')

Tiếp đó, mở file flask-customer/mycustomer/__init__.py và thêm dòng code sau để đăng ký blueprint với app:

...

def create_app(test_config=None):
    # tạo app 
    app = Flask(__name__, instance_relative_config=True)
    ...
    ...
    
    from . import customer
    app.register_blueprint(customer.bp)

    # Thêm 2 dòng dưới đây:
    from . import invoice 
    app.register_blueprint(invoice.bp)

    return app

Chức năng tạo invoice mới

Mở blueprint của invoice (invoice.py) và thêm route create phục vụ cho việc tạo invoice mới:

# tạo blueprint cho module invoice
bp = Blueprint('invoice', __name__, url_prefix='/invoice')

# Thêm route mới từ block code này:
@bp.route('/create', methods=('GET', 'POST'))
def create():
    # Nếu là submit form từ giao diện, xử lý lấy data và insert vào csdl
    if request.method == 'POST':
        # lấy data từ form mà user submit 
        customer_id = request.form['customer_id']
        invoice_date = request.form['invoice_date']
        description = request.form['description']
        sub_total = request.form['sub_total']
        discount = request.form['discount']
        tax = request.form['tax']
        error = None

        # kiểm tra các trường bắt buộc cần có:
        if not customer_id:
            error = 'Customer is required.'
        if not invoice_date:
            error = 'Invoice date is required.'
        if not description:
            error = 'Invoice description is required.'
        if not sub_total:
            error = 'Invoice subtotal is required.'


        if error is not None:
            flash(error)
        else:
            # nếu không có lỗi thiếu data nào, tiếp tục insert vào cơ sở dữ liệu 
            db = get_db()
            cur = db.execute(
                'INSERT INTO invoice (customer_id, invoice_date, description, sub_total, discount, tax)'
                ' VALUES (?, ?, ?, ?, ?, ?)',
                (customer_id, invoice_date, description, sub_total, discount, tax)
            )
            db.commit()
            # sau khi insert xong, đưa user về trang xem khách hàng, 
            # tại trang đó chúng ta cũng sẽ liệt kê ra các invoice đã nhập cho khách hàng này 
            return redirect(url_for('customer.view', id=customer_id))

    # route được gọi nhưng không phải qua phương thức POST.
    # Tức là trường hợp hiển thị form cho user xem và điền, nên cần lấy thông tin và đưa user về template create của invoice:
    # get list of customer
    customer_list = customer.list_customer()
    return render_template('invoice/create.html', customer_list=customer_list)

Ở bước tiếp theo, chúng ta cần tạo template cho view create invoice. Template này chứa form để người sử dụng nhập các thông tin cần thiết.

Như bạn thấy, khi route create của blueprint invoice được gọi, nếu gọi theo phương thức không phải là POST, chúng ta sẽ điều hướng cho user tới template create.

Template ‘templates/invoice/create.html’ cần có đoạn code sau:

{% extends 'base.html' %}

{% block scripts %}
  {{ super() }}
  {{ datepicker.loader() }} {# to load jQuery-ui #}
  {{ datepicker.picker(id=".datepicker") }}
{% endblock %}

{% block content %}
  <h1>New Invoice</h1>
  <form method="post">
    <div class="form-group">
      <label for="customer_id">Customer</label>
      <select name="customer_id" id="customer_id" class="form-control" required>
          {% for customer in customer_list %}
            <option value="{{ customer['id'] }}">{{ customer['name'] }}</option>
          {% endfor %}
      </select>
      <label for="invoice_date">Invoice Date</label>
      <input name="invoice_date" id="invoice_date" value="{{ request.form['invoice_date'] }}" class="form-control datepicker" required/>

      <label for="description">Description</label>
      <textarea name="description" id="description" class="form-control" required>{{ request.form['description'] }}</textarea>

      <label for="sub_total">Sub-total</label>
      <input name="sub_total" id="sub_total" value="{{ request.form['sub_total'] }}" class="form-control" required/>

      <label for="discount">Discount</label>
      <input name="discount" id="discount" value="{{ request.form['discount'] }}" class="form-control" />

      <label for="tax">Tax</label>
      <input name="tax" id="tax" value="{{ request.form['tax'] }}" class="form-control" />
    </div>
    <input type="submit" value="Save" class="btn btn-default">
  </form>
{% endblock %}
pip install Flask-Datepicker
{% block scripts %}
  {{ super() }}
  {{ datepicker.loader() }} {# to load jQuery-ui #}
  {{ datepicker.picker(id=".datepicker") }}
{% endblock %}
<input name="invoice_date" id="invoice_date" value="{{ request.form['invoice_date'] }}" class="form-control datepicker" required/>
      <select name="customer_id" id="customer_id" class="form-control" required>
          {% for customer in customer_list %}
            <option value="{{ customer['id'] }}">{{ customer['name'] }}</option>
          {% endfor %}
      </select>

Tạo menu New Invoice cho app.

Thêm đoạn code sau vào templates/base.html:

        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li><a href="{{ url_for('customer.create') }}">New Customer</a></li>
                <li><a href="{{ url_for('invoice.create') }}">New Invoice</a></li> <!-- THÊM MỚI DÒNG NÀY -->
                <li><a href="{{ url_for('customer.index') }}">List Customer</a></li>
            </ul>
        </div>

Reload lại trang web và bạn sẽ thấy menu item New Invoice. Click vào đó, điền form và bạn đã tạo thành công 1 invoice mới!

 

Bài tiếp theo: Python Flask: Liệt kê hóa đơn khi xem thông tin khách hàng

Exit mobile version