Phụ lụ c Cấu trúc thư mục dự án

Một phần của tài liệu Hệ thống quản lý quan hệ khách hàng (crm) (Trang 62 - 73)

STT Tên thư mục Diễn giải

1 db Dữ liệu lưu trữ.

2 template Thư mục chứa các thiết kế giao diện(*.html).

3 static Thư mục chứa hình ảnh, file CSS, Bootstrap, Js.

4 venv Thư mục chứa môi trường, các thư viện của chương trình.

5 migration Thư mục chứa các file chuyển đổi dữ liệu.

6 src Thư mục chứa các cấu hình và tính năng của hệ thống.

7 modules Thư mục chứa các file code tính năng của hệt thống.

8 config Thư mục chứa các file môi trường tùy chỉnh của hệ thống.

1.2. Blueprint

Blueprint trong Flask được áp dụng vào cấu trúc của hệ thống CRM là một cấu trúc luận lý đại diện cho một phần của ứng dụng. Blueprint trong hệ thống CRM bao gồm các thành phần như là định tuyến (route), hàm hiển thị, form, template và các file tĩnh và các yếu tố cần thiết liên quan đến một chức năng nhất định trong hệ thống. Được trình bày trong Hình 63 (mục màu tím).

Sau đây là sơ đồ chi tiết của blueprint xác thực chức năng khách hàng (client) trong Hệ thống quản lý quan hệ khách hàng (CRM):

Modules/

|_______ Client/  thư mục cho blueprint

Đề tài đồ án tốt nghiệp 2018 – 2021 Trường Đại học Bà Rịa – Vũng Tàu

SINH VIÊN THỰC HIỆN: BÙI VĂN HUÂN 63

| | |_______________ __init__.py  khởi tạo blueprint

| | |_______________ Client_form.py  form thông tin khách hàng

| |_____ Templates/  blueprint templates

| | |_______________ Add-client.html | | |_______________ Client.html | | |_______________ Client-details.html

| |_____ __init__.py  nơi đăng ký blueprint

| |_____ Client_controller.py  định tuyến chức năng client

| |_____ Client_models.py  tạo các class để quản lý ở

Client_controller.py

|__init__.py  nơi đăng ký blueprint

Định nghĩa các định tuyến cho blueprint trong hệ thống CRM, sử dụng decorator @client_module.route thay vì @app.route. Bên cạnh đó, cần phải cập nhật các lời gọi hàm url_for() để tạo ra các URL.

Đối với các hàm hiển thị được định nghĩa trực tiếp từ ứng dụng, tham số đầu tiên sẽ là tên hàm hiển thị. Nhưng đối với các định tuyến được định nghĩa bên trong blueprint được sử dụng trong CRM, tham số này bao gồm cả tên của blueprint và tên hàm hiển thị và được ngăn cách bằng dấu chấm.

Cụ thể khi lập trình phải thay thế tất cả các lời gọi hàm url_for('client') với url_for('client.add') và tương tự như vậy cho các trường hợp khác. Đăng ký blueprint khách hàng (client) với ứng dụng như sau:

Khi gọi hàm register_blueprint() trong hệ thống với chức năng client, sử dụng một tham số mới là url_prefix. Tham số này là tùy chọn, nhưng Flask cho phép kết nối một blueprint vào một tiền tố URL, nhờ đó mọi URL trong blueprint client sẽ được bắt đầu với tiền tố này. Mục đích áp dụng để phân biệt rõ các URL giữa các blueprint khác nhau trong hệ thống CRM.

Trong trường hợp này, mọi URL từ blueprint khách hàng sẽ được bắt đầu với tiền tố /client. Ví dụ như URL cho trang tạo thông tin khách hàng sẽ là http://localhost:5000/client/add. Bởi vì hệ thống sử dụng hàm url_for() để tạo ra các URL, tất cả các URL trong blueprint này sẽ được kết hợp với tiền tố này một cách tự động.

2. Code xử lý

Code xử lý trong hệ thống CRM bao gồm các chức năng xem, thêm, xóa, sửa thơng tin trong các chức năng tương ứng như xem danh sách thông tin khách hàng, xem thông tin chi tiết khách hàng, tạo thông tin khách hàng, sửa thơng tin khách hàng, xóa thơng tin khách hàng. Q trình thêm, xóa, sửa đổi với các mục khác thực hiện tương tự …

Ngoài ra, mẫu code xử lý với tính năng quản lý khách hàng (client) được sử dụng để diễn giải cách thức xử lý trong hệ thống CRM.

Đề tài đồ án tốt nghiệp 2018 – 2021 Trường Đại học Bà Rịa – Vũng Tàu

SINH VIÊN THỰC HIỆN: BÙI VĂN HUÂN 64

2.1. Xem danh sách thông tin khách hàng

2.1.1. Client Model - Tạo các class để quản lý trong Client Controller

import json

from datetime import datetime

from sqlalchemy import ForeignKey, Sequence from sqlalchemy.orm import relationship from src import db

class Client(db.Model):

__table_args__ = {'extend_existing': True}

id = db.Column(db.Integer, Sequence('client_id_seg'), primary_key=True) name = db.Column(db.String(50), nullable=False)

phone = db.Column(db.String(50), nullable=False) email = db.Column(db.String(100), nullable=False) website = db.Column(db.String(50), nullable=True) vat_number = db.Column(db.String(50), nullable=True) address = db.Column(db.String(100), nullable=True) zip_code = db.Column(db.String(50), nullable=True)

user_id = db.Column(db.Integer, ForeignKey('user.email')) user = relationship('User', backref='clients')

client_status_id = db.Column(db.Integer, ForeignKey('client_status.id')) client_status = relationship('ClientStatus', backref='clients')

country_id = db.Column(db.Integer, ForeignKey('country.id')) country = relationship('Country', backref='clients')

currency_id = db.Column(db.Integer, ForeignKey('currency.id')) currency = relationship('Currency', backref='clients')

def __init__(self, name: str, phone: str, email: str, website: str, vat_number: str, address: str, zip_code: str,

client_status_id: str, country_id: str, currency_id: str):

"""

The constructor for Client model. :param name: The company's name

:param phone: The company's phone number :param email: The email address of the company

:param website: The website url address of the company :param vat_number: The VAT number of the company :param address: The main address of the company :param zip_code: The ZIP Code unit

:param client_status_id: The client status is active or unActive :param country_id: The country's company

:param currency_id: The currency's unit """ self.name = name self.phone = phone self.email = email self.website = website self.vat_number = vat_number self.address = address self.zip_code = zip_code self.client_status_id = client_status_id self.country_id = country_id

Đề tài đồ án tốt nghiệp 2018 – 2021 Trường Đại học Bà Rịa – Vũng Tàu

SINH VIÊN THỰC HIỆN: BÙI VĂN HUÂN 65

self.currency_id = currency_id

def get_status_class(self):

if self.client_status_id == 1:

return "border border-green-500 text-green-700"

else:

return "border border-red-500 text-red-700"

class ClientStatus(db.Model):

id = db.Column(db.Integer, Sequence('client_status_id_seq'),

primary_key=True)

text = db.Column(db.String(50), nullable=False)

def __repr__(self):

return '<Client Status: {} with {}>'.format(self.id, self.text)

class JsonEcodedDict(db.TypeDecorator):

impl = db.Text

def process_bind_param(self, value, dialect): if value is None:

return '{}'

else:

return json.dumps(value)

def process_result_value(self, value, dialect): if value is None:

return {} else:

return json.loads(value)

class ClientOrder(db.Model):

__table_args__ = {'extend_existing': True}

id = db.Column(db.Integer, Sequence('client_order_id_seg'),

primary_key=True)

invoice = db.Column(db.String(50), unique=True, nullable=False) status = db.Column(db.String(50), default='Pending', nullable=False) date_created = db.Column(db.DateTime, default=datetime.utcnow,

nullable=False)

orders = db.Column(JsonEcodedDict)

client_id = db.Column(db.Integer, ForeignKey('client.id'), nullable=False) client = relationship('Client', backref='client_orders')

def __repr__(self):

return '<Client Order: {} with {}>'.format(self.invoice)

class ClientOrderHistory(db.Model):

id = db.Column(db.Integer, primary_key=True)

invoice = db.Column(db.String(20), unique=False,nullable=False) status = db.Column(db.String(20), default='Pending', nullable=False) customer_id = db.Column(db.Integer, unique=False, nullable=False) customer_name = db.Column(db.String(50), unique=False)

country = db.Column(db.String(50), unique=False) city = db.Column(db.String(50), unique=False) contact = db.Column(db.String(50), unique=False) zipcode = db.Column(db.String(50), unique=False)

Đề tài đồ án tốt nghiệp 2018 – 2021 Trường Đại học Bà Rịa – Vũng Tàu

SINH VIÊN THỰC HIỆN: BÙI VĂN HUÂN 66

product_id = db.Column(db.Integer, unique=False, nullable=False) product_name = db.Column(db.String(80), nullable=False)

product_price = db.Column(db.Numeric(10, 2), nullable=False) product_quantity = db.Column(db.Integer, nullable=False)

product_detail = db.Column(db.String(80), default='None', nullable=False) product_brand = db.Column(db.String(50), default='None', unique=False) product_category = db.Column(db.String(50), default='None', unique=False) payment_method = db.Column(db.String(50), default='Cash', unique=False) product_delivered= db.Column(db.String(20), default='False', unique=False) delivered_time = db.Column(db.DateTime, default=datetime.utcnow,

nullable=False)

order_date = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) product_cancel = db.Column(db.Boolean, nullable=False, default=False)

def __repr__(self):

return'<ClientOrder %r>' % self.invoice

2.1.2. Client Controller – Định tuyến chức năng Client

from flask import Blueprint, render_template, redirect, flash, session, url_for,

request, make_response

from flask_login import login_required, current_user from src import db

from src.main.modules.client import Client, ClientStatus, ClientOrder from src.main.modules.client.forms import ClientForm, ClientStatusForm from src.main.modules.country import Country

from src.main.modules.currency import Currency

import secrets import pdfkit

client_module = Blueprint('client', __name__, static_folder='static',

template_folder='templates')

@client_module.route('/', methods=['GET', 'POST']) @login_required

def client():

if current_user.is_authenticated:

return render_template('client.html', user=current_user) return redirect('/')

@client_module.route('/add', methods=['GET', 'POST']) @login_required

def add():

form = ClientForm()

# select customer status form client status table

form.inputClientStatus.choices = [(p.id, p.text) for p in

db.session.query(ClientStatus).all()]

# select currency unit form currency table

form.inputCurrency.choices = [(p.id, p.name) for p in

db.session.query(Currency).all()]

# select country form country table

form.inputCountry.choices = [(p.id, p.name) for p in

Đề tài đồ án tốt nghiệp 2018 – 2021 Trường Đại học Bà Rịa – Vũng Tàu

SINH VIÊN THỰC HIỆN: BÙI VĂN HUÂN 67

if form.validate_on_submit(): name = form.inputName.data phone = form.inputPhone.data email = form.inputEmail.data website = form.inputWebsite.data vat_number = form.inputVatNumber.data address = form.inputAddress.data zip_code = form.inputZIPCode.data client_status_id = form.inputClientStatus.data country_id = form.inputCountry.data currency_id = form.inputCurrency.data

client = Client(name=name, phone=phone, email=email, website=website,

vat_number=vat_number, address=address,

zip_code=zip_code, client_status_id=client_status_id, country_id=country_id,

currency_id=currency_id)

# add user email to owner customer

client.user = current_user

db.session.add(client) db.session.commit()

return redirect('/client')

form.inputClientStatus.render_kw = {'readonly': 'true', 'style': 'pointer-

events: none'}

return render_template('add-client.html', form=form, user=current_user)

@client_module.route('/edit/<int:id>', methods=['GET', 'POST']) @login_required

def edit(id):

form = ClientForm()

# re-index customer status form client status table

# on get request -- showing the form view

form.inputClientStatus.choices = [(p.id, p.text) for p in

db.session.query(ClientStatus).all()]

# re-index currency unit form currency table

form.inputCurrency.choices = [(p.id, p.name) for p in

db.session.query(Currency).all()]

# re-index country form country table

form.inputCountry.choices = [(p.id, p.name) for p in

db.session.query(Country).all()] the_client = db.session.query(Client).get(id) if form.validate_on_submit(): the_client.name = form.inputName.data the_client.phone = form.inputPhone.data the_client.email = form.inputEmail.data the_client.website = form.inputWebsite.data the_client.vat_number = form.inputVatNumber.data the_client.address = form.inputAddress.data the_client.zip_code = form.inputZIPCode.data the_client.client_status_id = form.inputClientStatus.data the_client.country_id = form.inputCountry.data the_client.currency_id = form.inputCurrency.data db.session.commit()

Đề tài đồ án tốt nghiệp 2018 – 2021 Trường Đại học Bà Rịa – Vũng Tàu

SINH VIÊN THỰC HIỆN: BÙI VĂN HUÂN 68

return redirect('/client') form.inputName.default = the_client.name form.inputPhone.default = the_client.phone form.inputEmail.default = the_client.email form.inputWebsite.default = the_client.website form.inputVatNumber.default = the_client.vat_number form.inputAddress.default = the_client.address form.inputZIPCode.default = the_client.zip_code form.inputClientStatus.default = the_client.client_status_id form.inputCountry.default = the_client.country_id form.inputCurrency.default = the_client.currency_id form.process()

return render_template('/add-client.html', form=form, user=current_user)

@client_module.route('/delete/<int:id>', methods=['GET', 'POST']) @login_required def delete(id): the_client = db.session.query(Client).filter_by(id=id).first() db.session.delete(the_client) db.session.commit() return redirect(f"/client") @client_module.route('/view/<int:id>', methods=['GET']) @login_required def view(id): if current_user.is_authenticated: the_client = db.session.query(Client).get(id) if not the_client.user == current_user:

flash('You don\'t have any customer information') return redirect('/')

return render_template('client-details.html', client=the_client,

user=current_user)

return redirect('/')

def update_shopping_cart():

for key, shopping in session['Shoppingcart'].items(): session.modified = True

del shopping['colors']

return update_shopping_cart

@client_module.route('/get-order', methods=['GET', 'POST']) @login_required def get_order(): if current_user.is_authenticated: # client_id = current_user.email client_id = request.form.get('client_id') invoice = secrets.token_hex(5) update_shopping_cart try:

the_order = ClientOrder(invoice=invoice, client_id=client_id,

orders=session['Shoppingcart'])

Đề tài đồ án tốt nghiệp 2018 – 2021 Trường Đại học Bà Rịa – Vũng Tàu

SINH VIÊN THỰC HIỆN: BÙI VĂN HUÂN 69

db.session.commit()

session.pop('Shoppingcart')

flash('Your order has been sent successfully', 'success') return redirect(url_for('orders', invoice=invoice))

except Exception as e: print(e)

flash('Some thing went wrong while get order', 'danger') return redirect(url_for('pos.view_cart')) @client_module.route('/orders/<invoice>') @login_required def orders(invoice): if current_user.is_authenticated: grandTotal = 0 subTotal = 0 orders = ClientOrder.query.filter_by(invoice=invoice).order_by(ClientOrder.id.desc()).fir st()

for _key, product in orders.orders.items():

discount = (product['discount'] / 100) * float(product['price']) subTotal += float(product['price']) * int(product['quantity']) subTotal -= discount

tax = ("%.2f" % (.06 * float(subTotal)))

grandTotal = ("%.2f" % (1.06 * float(subTotal))) else:

return redirect(url_for('/'))

return render_template('/client-order.html', invoice=invoice, tax=tax,

subTotal=subTotal, grandTotal=grandTotal, orders=orders, user=current_user) @client_module.route('/get_pdf/<invoice>', methods=['POST']) @login_required def get_pdf(invoice): if current_user.is_authenticated: grandTotal = 0 subTotal = 0 if request.method == "POST": orders = ClientOrder.query.filter_by(invoice=invoice).order_by(ClientOrder.id.desc()).fir st()

for _key, product in orders.orders.items():

discount = (product['discount']/100) * float(product['price']) subTotal += float(product['price']) * int(product['quantity']) subTotal -= discount

tax = ("%.2f" % (.06 * float(subTotal)))

grandTotal = float("%.2f" % (1.06 * subTotal))

rendered = render_template('/pdf.html', invoice=invoice, tax=tax,

grandTotal=grandTotal, orders=orders) pdf = pdfkit.from_string(rendered, False) response = make_response(pdf) response.headers['content-Type'] ='application/pdf' response.headers['content-Disposition'] ='inline; filename='+invoice+'.pdf' return response return request(url_for('orders'))

Đề tài đồ án tốt nghiệp 2018 – 2021 Trường Đại học Bà Rịa – Vũng Tàu

SINH VIÊN THỰC HIỆN: BÙI VĂN HUÂN 70

2.1.3. Đăng ký Blueprint

from .client_model import Client, ClientStatus, ClientOrder, ClientOrderHistory from .client_controller import client_module

2.1.4. Form thông tin khách hàng

from flask_wtf import FlaskForm

from wtforms import StringField, SelectField from wtforms.validators import DataRequired

class ClientForm(FlaskForm):

inputName = StringField(label='Name', validators=[

DataRequired(message='Please fill out the name field'),

], render_kw={"placeholder": "Client name"})

inputPhone = StringField(label='Phone Number', validators=[

DataRequired(message='Please fill out the phone number field'),

], render_kw={"placeholder": "Phone number"})

inputEmail = StringField(label='Email address', validators=[

DataRequired(message='Please fill out the email address field'),

], render_kw={"placeholder": "Email address"})

inputWebsite = StringField(label='Website', validators=[

DataRequired(message='Please fill out the website url field'),

], render_kw={"placeholder": "Website URL"})

inputVatNumber = StringField(label='VAT Number', validators=[ DataRequired(message='Please fill out the VAT number field'),

], render_kw={"placeholder": "VAT number"})

inputAddress = StringField(label='Address', validators=[ DataRequired(message='Please fill out the address field'),

], render_kw={"placeholder": "Address"})

inputZIPCode = StringField(label='ZIP Code', validators=[ DataRequired(message='Please fill out the ZIP code field'),

], render_kw={"placeholder": "ZIP code"})

inputClientStatus = SelectField('Client Status', coerce=int)

inputCountry = SelectField('Country', coerce=int)

inputCurrency = SelectField('Currency', coerce=int)

class ClientStatusForm(FlaskForm):

inputText = StringField(label='Status', validators=[

DataRequired(message='Please fill out the status field'),

], render_kw={"placeholder": "Status"})

2.1.5. Khởi tạo Blueprint

from .client_form import ClientForm, ClientStatusForm

2.1.6. Khai báo các định tuyến được định nghĩa bên trong Blueprint

Sau khi lập trình xử lý và định tuyến các class trong chức năng quản lý thông tin khách hàng, để chức năng hoạt động được, cần phải khai báo các định tuyến được định nghĩa bên trong blueprint client của hệ thống.

Đề tài đồ án tốt nghiệp 2018 – 2021 Trường Đại học Bà Rịa – Vũng Tàu

SINH VIÊN THỰC HIỆN: BÙI VĂN HUÂN 71

2.1.6.1. Main/__init__.py

from flask import Flask

class App(Flask):

def __init__(self, instance_path: str): super(App, self).__init__(

import_name=__name__,

instance_path=instance_path, instance_relative_config=True )

# assigning the base templates & static folder

self.template_folder = './base/templates' self.static_folder = './base/static' # loading environment variables self.load_environment_variables()

# registering essential partials for the app

self.register_blueprints()

self.register_login_manager()

def register_blueprints(self): """

Registering the app's blueprints. """

from src.main.modules.client import client_module

self.register_blueprint(client_module, url_prefix="/client")

def register_login_manager(self): # adding login manager

from flask_login import LoginManager

login_manager = LoginManager() login_manager.login_view = "auth.login" login_manager.init_app(self) @login_manager.user_loader def load_user(email): # registering user_loader

from src.main.modules.user import User

return User.query.get(email)

def load_environment_variables(self): """

Loading the configured environment variables. """

# Load the default configuration (../config/default.py)

self.config.from_object('config.default')

# Load the file specified by the APP_CONFIG_FILE environment variable

# Variables defined here will override those in the default configuration

Đề tài đồ án tốt nghiệp 2018 – 2021 Trường Đại học Bà Rịa – Vũng Tàu

SINH VIÊN THỰC HIỆN: BÙI VĂN HUÂN 72

2.1.6.2. Src/__init__.py

import sys import os

from pathlib import Path def add_sys_paths():

print('\n[ADDING PATHS TO THE PYTHON ENVIRONMENT...]')

# getting the current file's absolute path

CURRENT_FILE_ABSOLUTE_PATH = Path(__file__).absolute()

# WORKING_DIR: src

WORKING_DIR = os.path.abspath(os.path.join(CURRENT_FILE_ABSOLUTE_PATH, '../'))

# ROOT_DIR (includes the: src ; scripts ; venv ; ..

ROOT_DIR = os.path.abspath(os.path.join(CURRENT_FILE_ABSOLUTE_PATH, '../../'))

# appending the WORKING_DIR, ROOT_DIR to the python environment

sys.path.append(WORKING_DIR)

sys.path.append(ROOT_DIR)

print('\n[PATHS IN THE PYTHON ENVIRONMENT...]:') print('\n'.join(sys.path), '\n')

return WORKING_DIR, ROOT_DIR

def create_app():

# initializing the app

print("\n[INITIALIZING THE APP...]")

from src.main import App

app = App(instance_path=add_sys_paths()[0])

print("\n[INITIALIZING THE DATABASE...]") db.init_app(app=app)

# migrating Models to DB

from flask_migrate import Migrate

print("\n\n[MIGRATING THE DATABASE...]") migrate = Migrate()

with app.app_context():

# allow dropping column for sqlite

if db.engine.url.drivername == 'sqlite': migrate.init_app(app, db, render_as_batch=True) else: migrate.init_app(app, db) # importing models import src.main.modules.user.user_model

# place your new model (want to be created in the app.sqlite) here

import src.main.modules.client.client_model

print('\n\n[NEW APP RETURNED...]') return app

Đề tài đồ án tốt nghiệp 2018 – 2021 Trường Đại học Bà Rịa – Vũng Tàu

SINH VIÊN THỰC HIỆN: BÙI VĂN HUÂN 73

print("\n[DEFINING THE DATABASE INSTANCE...]") from flask_sqlalchemy import SQLAlchemy

Một phần của tài liệu Hệ thống quản lý quan hệ khách hàng (crm) (Trang 62 - 73)