Deployed website: Link to website
Card number for payment testing: 4242424242424242
"WoWder" is an e-commerce website that allows customers to buy various products. It has a login system, a shopping cart, a checkout system, and a payment system. Loyal customers can get discounts on their purchases by using the coupon system. This website has a notification system that allows users to be notified when a product is available if they submit a request. In addition, the website has a personnel functionality that allows admin, manager, logistics, and other personnel to manage the website. The decision to implement multiple personnel roles was made due to the desire to create the project as scalable as possible and reusable in the real world.
The website was created to be eye-catching and user-friendly. The user is given plenty of choices to choose from when they are shopping. The emphasis is on the user experience; the user can navigate the website easily to fulfill users' goals. The website is designed to be easy to use and easy to understand. Additionally, the website attracts customers to become a part of loyalty programs by giving them additional discounts on their purchases. It also handles all personnel functionality moving from admin to manager to logistics to other personnel. Business goals were to make the website as scalable as possible and reusable in the real world so that the store personnel could use it according to their position in the company.
This website is intended for people interested in purchasing products online, starting from bread and finishing electronics, such as TV, smartphones, or other products. It was also aimed to lure people into using this website in their daily lives. This was achieved by implementing a discount system for all customers, additional discounts for loyal customers, and a notification system that allows users to be notified when a product is available. This website may hold many products, and it is essential to make sure that the website is easy to use and navigate.
Issue ID | User Story |
---|---|
#1 | As a First Time Visitor, I want to be able to register my account, so that I can learn the benefits of the app as a user. |
#2 | As a First Time Visitor, I want to be able to easily understand the main purpose of the app, so that I can learn more about this app. |
#3 | As a First Time Visitor, I want to be able to easily navigate through the app, so that I can find the content. |
#4 | As a First Time Visitor, I want to be able to find the app useful, so that I can use it according to my needs. |
#5 | As a First Time Visitor, I want to be informed clearly if I am making any errors when registering my account, so that I can be able to fix any errors quickly if I make some. |
#16 | As a First Time Visitor, I want to be able to see the list of products, so that I can learn the benefits of the app as a user. |
#78 | As a First Time Visitor, I want to be able to register with social media, so that I can easily register my account or login. (Future feature) |
Issue ID | User Story |
---|---|
#6 | As a Regular User, I want to be able to access my account without having to log in every time, so that I can quickly buy a product that I need. |
#7 | As a Regular User, I want to be sure that my account details are protected, so that I can safely make purchases. |
#8 | As a Regular User, I want to be able to view my data, so that I can easily check my account details and past orders. |
#9 | As a Regular User, I want to be able to search for a product, so that I can get the most appropriate choices, so that I am likely to find what I am looking for. (Future development) |
#10 | As a Regular User, I want to be able to add, edit, and save my address data, so that I can make an order faster. |
#11 | As a Regular User, I want to be able to search products by category, tag, or text search, so that I can find a product faster. |
#12 | As a Regular User, I want to be able to sort products by category, so that I can find the best option to buy. |
#13 | As a Regular User, I want to be able to leave a product review, so that I can share my feedback. |
#14 | As a Regular Visitor, I want to be able to see ratings and reviews on a product, so that I can make a prudent decision before buying it. |
#15 | As a Regular User, I want to be able to see recommendations underneath the product that I am reviewing, so that I can have a choice to pick from. (future development) |
#17 | As a Regular User, I want to be able to see the list of products, so that I can learn the benefits of the app as a user. |
#18 | As a Regular Customer, I want to be able to see discount offers, so that I can buy products with a discount and save some money. |
#19 | As a Customer, I want to be able to see if the product is about to go out of stock, so that I can plan my purchases. |
#20 | As a Customer, I want to be able to see if the product is out of stock, so that I can be informed in advance that the item is not available and save my time. |
#21 | As a Customer, I want to be able to See a full product description (image, name, description, options, price, discount), so that I can understand whether I want to buy this product. |
#22 | As a Customer, I want to be able to see the product’s options (size, color, height), so that I can understand whether the option of the product suits me. |
#23 | As a Customer, I want to be able to choose, edit product’s options (size, color, height), so that I can choose an appropriate option of the product. |
#24 | As a Customer, I want to be able to ask for the notification from the shop if I want to purchase a product that is out of stock or about to finish, so that I can buy a product that I really want. |
#25 | As a Customer, I want to be able to get an email notification from the shop about special offers, promotions, discounts, so that I can make purchases cheaper. |
#26 | As a Customer, I want to be able to get an email notification from the shop if the product that I wanted and was out of stock came back to the shop, so that I can buy a product that I really want when it is available. |
#27 | As a Customer, I want to be able to add product to my wish list, so that I can buy it later. |
#28 | As a Customer, I want to be able to remove products to my wish list, so that I can keep my wish list up to date. |
#29 | As a Customer, I want to be able to view products on my wish list, so that I can plan my purchases. |
#30 | As a Customer, I want to be able to increase/reduce the number of product items in my bag that I want, so that I can buy a number of items that I want. |
#31 | As a Customer, I want to be able to add products to my bag, so that I can easily save products in the bag. |
#32 | As a Customer, I want to be able to see the counted total cost of the product, so that I can see how much I will spend. |
#33 | As a Customer, I want to be able to see the total cost of the products in the bag, so that I can see how much I will spend. |
#35 | As a Customer, I want to be able to remove the product from my bag, so that I can change my mind not to buy a particular product at the last moment. |
#36 | As a Customer, I want to be able to see messages from the shop, so that I can understand whether an item is actually added to the bag or removed. |
#37 | As a Customer, I want to be able to contact shop staff immediately, so that I can get an answer to my questions quickly. (Future development) |
#38 | As a Visitor, I want to be able to send photos of a product, so that I can provide proof if there are any probable issues with the delivered product (wrong color, size, or damage). |
#39 | As a Customer, I want to be able to living images report for review, so that I can share my real experience. (future development) |
#40 | As a Customer who made a purchase, I want to be able to review the order status, so that I can understand where my purchase is. |
#41 | As a Customer who made a purchase, I want to be able to see order confirmation after checkout, so that I can see what I bought. |
#42 | As a Customer who made a purchase, I want to be able to receive an email confirmation of my purchase, so that I can have email proof of purchase. |
#51 | As a Customer, I want to be able to receive live messages including images from customers, so that I can provide answers to customers’ questions and assist them in resolving any probable problems with purchases. (Future development) |
#75 | As a Regular user, I want to be able to change my profile data, so that I can keep my profile up to date. |
#76 | As a Regular User, I want to be able to add, edit, and delete addresses, so that I can be sure that I will receive my order at the correct address. |
#77 | As a Regular User, I want to be able to change primary address, so that I can set the primary address without editing it. |
Issue ID | User Story |
---|---|
#43 | As a Manager, I want to be able to add, edit, and delete category, so that I can keep products’ categories up to date. |
#44 | As a Manager, I want to be able to add, edit, and delete products, so that I can keep shop products up to date. |
#45 | As a Manager, I want to be able to add, edit, delete tags, so that I can keep products’ sorting up to date and, as a result, increase sales. |
#46 | As a Manager, I want to be able to change tags for a product, so that I can increase sales for a particular product. |
#47 | As a Manager, I want to be able to Add, edit, and delete products’ options (attributes and values), so that I can keep products’ options up to date. |
#49 | As a Manager, I want to be able to send emails to customers about future promotions, so that I can increase sales by notifying customers about promotions. |
#50 | As a Manager, I want to be able to view customers' data, so that I can contact customers if needed and solve possible problems with purchases. |
#52 | As a Manager, I want to be able to answer to customers immediately, so that I can satisfy customers' needs. (Future development) |
#53 | As a Manager, I want to be able to sort and review live support chats, so that I can get access to the previous conversation between a customer and other tech support managers. (Future development) |
#184 | As a Manager, I want to be able to control product status (active/not active), so that I can keep shop stock up to date. |
#186 | As a Manager, I want to be able to Review user’s requests on a product that is out of stock, so that I can understand customers' urgent needs. |
#187 | As a Manager, I want to be able to review user’s requests on a product that is about to go out of stock, so that I can understand customers' urgent needs and products’ popularity. |
#188 | As a Manager, I want to be able to send emails to customers who left notification letters about products coming back to the shop, so that I can enhance customer loyalty and increase sales. |
#189 | As a Manager, I want to be able to send emails to customers who left notification letters about products that are no longer in the shop, so that I can enhance customer loyalty and increase sales. (Future development) |
#190 | As a Manager, I want to be able to review and edit orders’ status, so that I can control customers’ orders and sales. (Only admin can render) |
#191 | As a Manager, I want to be able to sort products by rating, sales, and reviews, so that I can understand customers' preferences and increase sales. (Future development) |
#192 | As a Manager, I want to be able to sort products by stock number, so that I can control stock numbers. |
#193 | As a Manager, I want to be able to create sales statistics, so that I can implement new sales strategies if needed. (Future development) |
#194 | As a Manager, I want to be able to review customers’ orders, so that I can control orders. |
#195 | As a Manager, I want to be able to sort orders by date and status, so that I can prioritize orders. |
#196 | As a Manager, I want to be able to learn and sort customers’ reviews left by date, rating, and username, so that I can understand customers' feedback and needs. (Future development) |
#197 | As a Manager, I want to be able to display new products label, so that I can be sure that customers will be aware of new products in the shop. |
#198 | As a Manager, I want to be able to add/render store purchases, so that I can keep the stock app to date. (Future development) |
#199 | As a Manager, I want to be able to control my suppliers' data, so that I can easily connect with them. (Future development) |
#200 | As a Manager, I want to be able to Allow customers to leave reviews only after they receive a product, so that I can control that reviews are relevant. |
Issue ID | User Story |
---|---|
#289 | As a Logistics Manager or Admin, I want to be able to render order status, so that I can track at which stage the order is in. |
Issue ID | User Story |
---|---|
#48 | As an Admin, I want to be able to add promotions, so that I can increase sales and enhance customers’ loyalty. |
#92 | As an Admin, I want to be able to get a visible notice about products that are running in stock, so that I can keep stock up to date. |
#93 | As an Admin, I want to be able to be aware of units sold, so that I can be aware of sales on a particular product. |
#94 | As an Admin, I want to be able to get a visible notice if there is some inconsistency in sales, so that I can be aware of possible stealing. |
#95 | As an Admin, I want to be able to get a visible notice if there a product is not salable, so that I can make a decision about stopping purchasing this product for my store. |
#185 | As an Admin, I want to be able to send special discounts email notifications, so that I can inform loyal customers about special offers. |
All User stories were initially created in LibreOffice Calc PDF version PDF version
The Business Model is B2C, meaning that the company sells products to customers only. It focuses on individual transactions only.
The "WoWder" store is aimed at a diverse group of people considering their needs and wants. As everyone has different needs and wants, the store provides everyone the best possible service. The age criteria on this website are customers who are 18 and older. The emphasis was made on providing a good user experience for the customers by allowing them to find the products they are looking for, giving them all available options of these products, and offering the best possible service.
The "Wowder" store is aimed at companies interested in selling products online. It may be a small company or a large one. Moreover, the company's potential growth is considered by implementing full personnel functionality with the ability to render all data in the store, starting from cotegories and tags and finishing product inventory and orders.
"WoWder" is an online store app that is challenging to compete with due to its wide variety of product offerings and low-cost products.
- a wide variety of product offerings;
- product availability;
- product quality;
- product choice;
- good user experience;
- good service;
- good sales;
- good customer loyalty;
- unique customer service (customer products' requests);
Newsletters were implemented in the email_notification app. The manager can create a newsletter and send it to all customers. It also handles sending newsletters about new loyalty programs with promo codes.
- News Letter
To send a newsletter to all customers, the manager can create a newsletter and send it to all customers. I have used Django EmailMultiAlternatives to send the newsletter.
- Promo Codes
To send a newsletter about new loyalty programs with promo codes to all customers, the manager can create a newsletter using the same form with a promo code field filled with a legend. Django EmailMultiAlternatives is also responsible for it.
Facebook is essential for the store to be able to reach customers. According to the statistics, the store has a good reach among people who prefer to use Facebook. Facebook has excellent coverage worldwide among people who can purchase products online.
"WoWder" Facebook page is for marketing purposes to post adverts and exciting content and engage users.
Instagram attracts people of younger ages and is essential for the store to be able to reach this age group. According to the statistics, 90% of Instagram users are under 35 years old. See report
"WoWder" Instagram page is for marketing purposes to post adverts, interesting content, and engage users.
Twitter has become a place for people from different backgrounds to meet and discuss various topics! That is why "WoWder" presence is also essential on this platform.
"WoWder" Twitter page is for marketing purposes to post adverts, and exciting content, and start the discussion about the shop itself.
Chat support is planned to be implemented to provide the customers with the best possible service.
My application is already using ASGI instead of WSGI. Thus, it would be easier to implement chat support using Django Channels and Redis channel layers in the future.
This feature is fully dependent on the business requirements. And not all business requirements include this feature due to the customers' personal preferences.
Facebook and Twitter were not considered for future development at this stage as they require additional information from the business. Google is the most popular third-party registration service that is easy to implement; however, it is not available in all countries and regions (like China).
I want to implement PayPal payment system in the future as many customers prefer it. I am also considering adding Alipay and WeChat payment functionality to the existing stripe payment system as they are widely used in Asian countries like China and Thailand.
I want to add the suppliers' app in the future as it is a significant part of the business. The suppliers' app is a platform for businesses to manage suppliers and products they offer customers. It will highly increase the business' productivity and sales. The models for this app have already been planned and described in the DB architecture tables.
This feature requires more research time to implement, which is why it is not implemented at this stage.
Elastic search is necessary for the future development of the application as it may increase the search speed and the quality of search results.
This decision to postpone the this feature's implementation was necessary to make as a developer, I have to evaluate the necessity of all features and estimate the time for the development process and the time for the implementation of the features. Moreover, it is crucial to know the exact operating procedure of a real business. For example, understanding the business's product prioritization to set up the recommendation functionality will play a crucial role in the implementation process. Thus, I would like to leave those features as the opportunity to future learning and development.
This interesting feature has kept me curious during the development process as there are many different ways to calculate the delivery cost. At this stage, I decided to go with the idea of free delivery based in all Chinese e-commerce stores, where the cost is already included in the product's price. However, to make the application more flexible, I would like to consider the possibility of different delivery cost calculation methods and plan to consult with logistics specialists to get all the necessary information. We have to take into consideration various factors such as:
- The shipping company fees;
- the actual distance between a shop warehouse and the customer's address;
- The total weight of the products in the order;
- The parameters of the package (size, weight, etc.);
- The value of the products in the order (whether the order includes golden jewelry, TV, etc.);
- Payment method (whether the customer is paying with a credit card, PayPal, etc.);
- Tax rate;
- If the customer is located abroad, the border tax rate;
- Option to deliver by courier or by self-service;
- etc.
-
- Python 3.8.5: the primary language used to develop the server-side of the website.
- JS: the primary language used to develop interactive components of the website.
- HTML: the markup language used to create the website.
- CSS: the styling language used to style the website.
-
- Django: python framework used to create all the logic.
- jQuery: was used to control click events and sending AJAX requests.
- jQuery User Interface was used to create interactive elements.
-
- SQLite: was used as a development database.
- PostgreSQL: the database used to store all the data.
-
- Git: the version control system used to manage the code.
- Pip3: the package manager used to install the dependencies.
- Gunicorn: the web server used to run the website.
- Psycopg2: the database driver used to connect to the database.
- Django-allauth: the authentication library used to create the user accounts.
- Django-crispy-forms: was used to control the rendering behavior of Django forms.
- Render: was used to render the README file.
- GitHub: used to host the website's source code.
- VSCode: the IDE used to develop the website.
- Chrome DevTools: was used to debug the website.
- Font Awesome: was used to create the icons used in the website.
- Draw.io was used to make a flowchart for the README file.
- Coolors was used to make a color palette for the website.
- BGJar: was used to make a background images for the website.
- W3C Validator: was used to validate HTML5 code for the website.
- W3C CSS validator: was used to validate CSS code for the website.
- JShint: was used to validate JS code for the website.
- PEP8: was used to validate Python code for the website.
- geonames was used to get the country and city names.
- Multiple Video & Image Upload Plugin - jQuery Miv.js was used to upload multiple videos and images. Note: the plugin is using special characters in css and js files that I am aware about!
- stripe: was used to create the payment system.
- birme.net: was used to crop and center unsplash images.
- Sitemap Generator was used to create the sitemap.xml file.
- Privacy Policy Generator was used to create the privacy policy.
- Django-extensions was used to create a Entity-Relationship Diagram.
Please refer to the FEATURES.md file for all test-related documentation.
The design of the application is based on Material Design principles. The colors are chosen to be consistent with the Material Design principles The minimalistic approach was used to create something meaningful without moving out of the customer's focus. As this application is a multifunctional (provides full customer experience and business management) application and consists of many components, the decision to implement white spaces was made as it helps to create a more pleasant user experience. It also helps users, whether customers or personnel, to focus on the main content of the application.
The application's color scheme is based on the combination of bold and neutral colors. The bold colors are used to create a more vibrant and attractive user experience. The neutral colors are used to create a more calm and relaxed user experience.
The navbar and footer are colored with a dark purple color (#151422
) to emphasize the application's main content. The navbar for admin was colored with the purplish color (#4f378b
) to separate is from the main navbar and put an accent on the admin functionality. It also helps personnel to distinguish that they enter as personnel.
The color scheme for the text is quite simple to create a more readable user experience.
As the application is highly complex and has lots of functionality, I have decided to implement accent colors to help users navigate the application. The accent colors are used to create a more vibrant and attractive user experience.
The main font used in the application is Lato. During my learning experience, I found that the Lato font is straightforward and has perfect readability, increasing user experience. This font is also consistent with the color scheme.
To emphasize the importance of the text, the font-weight was set to 900. To make the accent on the buttons, the font-weight was set to 700. For the rest of the text, the font-weight was set to 400.
-
The main background image was generated with the user of the BGJar tool.
-
Images were downloaded from the websites listed in the Credits section. Content and Images
-
The main part is allocated to using icons from the font awesome website. Icons are essential for the user experience when it comes to multifunctional websites.
GitHub Project Management was used to manage the project. If it hadn't been for the GitHub project management, I wouldn't have been able to manage the development of the application. It helped me to prioritize the tasks and to keep track of my progress.
To understand some concepts, I created several flowchart diagrams.
- Sending stock email notifications to users who have left a request for a particular product inventory.
- Get discount after applying the coupon code for loyalty program.
- Payment system.
- During the earliest stages of the project, the database was created using SQLite.
- The database was then migrated to PostgreSQL.
Name | Database Key | Field Type | Validation |
---|---|---|---|
name | name | CharField | max_length=50, unique=True, blank=True, null=False, verbose_name='Role name' |
description | description | TextField | max_length=500, blank=True, null=True, verbose_name='Role description' |
When user signs up, a new profile is created.
Name | Database Key | Field Type | Validation |
---|---|---|---|
user | user | OneToOneField | User, on_delete=models.CASCADE, related_name='profile', verbose_name='User' |
first_name | first_name | CharField | max_length=50, blank=True, null=True, verbose_name='First name' |
last_name | last_name | CharField | max_length=50, blank=True, null=True, verbose_name='Last name' |
birthday | birthday | DateField | blank=True, null=True, verbose_name='Birthday' |
avatar | avatar | CloudinaryField | blank=True, null=True, verbose_name='Avatar' |
subscription | subscription | BooleanField | default=False, verbose_name='Subscription' |
role | role | ForeignKey | Role, default=1, on_delete=models.SET_NULL, null=True, verbose_name='Role' |
created_at | created_at | DateTimeField | auto_now_add=True, verbose_name='Created at' |
updated_at | updated_at | DateTimeField | auto_now=True, verbose_name='Updated at' |
Note: The role field is set to 1 by default. This is because the user is a customer by default. The role can be changed only from the admin panel. The decision to make it mandatory was made due to the store security reasons. Only the site admin can change the role of the user in order to prevent unauthorized access.
Users are encouraged to create their own addresses and set the default address for the fastest purchase.
Name | Database Key | Field Type | Validation |
---|---|---|---|
user | user | ForeignKey | User, on_delete=models.CASCADE, related_name='addresses', verbose_name='User' |
country | country | CharField | max_length=50, blank=False, null=False, verbose_name='Country' |
county_region | county_region | CharField | max_length=50, blank=False, null=False, verbose_name='County/region' |
city | city | CharField | max_length=50, blank=False, null=False, verbose_name='City' |
address_line | address_line | CharField | max_length=150, blank=False, null=False, verbose_name='Address line' |
zip_code | zip_code | CharField | max_length=10, blank=False, null=False, verbose_name='Zip code' |
phone_number | phone_number | CharField | max_length=15, blank=False, null=False, verbose_name='Phone' |
is_primary | is_primary | BooleanField | default=False, verbose_name='Is primary' |
created_at | created_at | DateTimeField | auto_now_add=True, verbose_name='Created at' |
updated_at | updated_at | DateTimeField | auto_now=True, verbose_name='Updated at' |
When the user signs up, a new wishlist is created.
Name | Database Key | Field Type | Validation |
---|---|---|---|
user | user | ForeignKey | User, on_delete=models.CASCADE, related_name='wishlist', verbose_name='User' |
products | products | ManyToManyField | Product, blank=True, related_name='wishlist', verbose_name='Products' |
created_at | created_at | DateTimeField | auto_now_add=True, verbose_name='Created at' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
name | name | CharField | max_length=100, unique=True, blank=False, null=False, verbose_name='Category name' |
slug | slug | SlugField | max_length=150, unique=True, blank=False, null=False, verbose_name='Category Slug' |
is_active | is_active | BooleanField | default=False, verbose_name='Is active' |
created_at | created_at | DateTimeField | auto_now_add=True, verbose_name='Created at' |
updated_at | updated_at | DateTimeField | auto_now=True, verbose_name='Updated at' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
name | name | CharField | max_length=100, unique=True, blank=False, null=False, verbose_name='Tag name' |
slug | slug | SlugField | max_length=150, unique=True, blank=False, null=False, verbose_name='Tag Slug' |
is_active | is_active | BooleanField | default=False, verbose_name='Is active' |
created_at | created_at | DateTimeField | auto_now_add=True, verbose_name='Created at' |
updated_at | updated_at | DateTimeField | auto_now=True, verbose_name='Updated at' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
name | name | CharField | max_length=100, unique=True, blank=False, null=False, verbose_name='Brand name' |
slug | slug | SlugField | max_length=150, unique=True, blank=False, null=False, verbose_name='Brand Slug' |
description | description | TextField | max_length=500, blank=False, null=False, verbose_name='Brand description' |
is_active | is_active | BooleanField | default=False, verbose_name='Is active' |
created_at | created_at | DateTimeField | auto_now_add=True, verbose_name='Created at' |
updated_at | updated_at | DateTimeField | auto_now=True, verbose_name='Updated at' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
name | name | CharField | max_length=100, unique=True, blank=False, null=False, verbose_name='Product name' |
slug | slug | SlugField | max_length=150, unique=True, blank=False, null=False, verbose_name='Product Slug' |
description | description | TextField | max_length=500, blank=False, null=False, verbose_name='Product description' |
category | category | ForeignKey | Category, on_delete=models.CASCADE, related_name='products', verbose_name='Category' |
tags | tags | ManyToManyField | Tag, related_name='products', verbose_name='Tags' |
brand | brand | ForeignKey | Brand, on_delete=models.CASCADE, related_name='products', verbose_name='Brand' |
is_active | is_active | BooleanField | default=False, verbose_name='Is active' |
created_at | created_at | DateTimeField | auto_now_add=True, verbose_name='Created at' |
updated_at | updated_at | DateTimeField | auto_now=True, verbose_name='Updated at' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
product | product | ForeignKey | Product, on_delete=models.CASCADE, related_name='images', verbose_name='Product' |
image | image | CloudinaryField | null=True, blank=True, verbose_name='Image' |
alt_text | alt_text | CharField | max_length=300, null=True, blank=True, verbose_name='Alt text' |
default_image | default_image | BooleanField | default=False, verbose_name='Default image' |
is_active | is_active | BooleanField | default=False, verbose_name='Is active' |
created_at | created_at | DateTimeField | auto_now_add=True, verbose_name='Created at' |
updated_at | updated_at | DateTimeField | auto_now=True, verbose_name='Updated at' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
name | name | CharField | max_length=255, unique=True, blank=False, null=False, verbose_name='Attribute name' |
description | description | TextField | max_length=500, blank=True, null=True, verbose_name='Attribute description' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
name | name | CharField | max_length=100, unique=True, blank=False, null=False, verbose_name='Product type name' |
slug | slug | SlugField | max_length=150, unique=True, blank=False, null=False, verbose_name='Product type Slug' |
product_type_attributes | product_type_attributes | ManyToManyField | ProductAttribute, related_name="product_type_attributes", through="ProductTypeAttribute", verbose_name='Product type attributes' |
description | description | TextField | max_length=500, blank=False, null=False, verbose_name='Product type description' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
product_attribute | product_attribute | ForeignKey | ProductAttribute, on_delete=models.CASCADE, related_name='product_attribute_values', verbose_name='Product attribute' |
attribute_value | attribute_value | CharField | max_length=255, blank=False, null=False, verbose_name='Attribute value' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
sku | sku | CharField | max_length=50, null=False, unique=True, blank=False, verbose_name='Stock Keeping Unit' |
upc | upc | CharField | max_length=12, null=False, unique=True, blank=False, verbose_name='Universal Product Code' |
product | product | ForeignKey | Product, on_delete=models.CASCADE, related_name='inventory', verbose_name='Product' |
product_type | product_type | ForeignKey | ProductType, on_delete=models.CASCADE, related_name='inventory', verbose_name='Product type' |
attribute_values | attribute_values | ManyToManyField | ProductAttributeValue, related_name="product_attribute_values", through="ProductAttributeValues", verbose_name='Attribute values' |
retail_price | retail_price | DecimalField | max_digits=9, decimal_places=2, null=False, blank=False, verbose_name='Retail price' |
store_price | store_price | DecimalField | max_digits=9, decimal_places=2, null=False, blank=False, verbose_name='Store price' |
sale_price | sale_price | DecimalField | max_digits=9, decimal_places=2, null=False, blank=False, verbose_name='Sale price' |
weight | weight | FloatField | null=False, blank=False, verbose_name='Product weight' |
is_active | is_active | BooleanField | default=False, verbose_name='Is active' |
created_at | created_at | DateTimeField | auto_now_add=True, verbose_name='Created at' |
updated_at | updated_at | DateTimeField | auto_now=True, verbose_name='Updated at' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
product_inventory | product_inventory | ForeignKey | ProductInventory, on_delete=models.CASCADE, related_name='stock', verbose_name='Product inventory' |
last_checked | last_checked | DateTimeField | null=True, blank=True, verbose_name='Last checked' |
units_variable | units_variable | IntegerField | default=0, null=False, blank=False, verbose_name='Units variable' |
units | units | IntegerField | default=0, null=False, blank=False, verbose_name='Units current' |
units_sold | units_sold | IntegerField | default=0, null=False, blank=False, verbose_name='Units sold' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
attributevalues | attributevalues | ForeignKey | ProductAttributeValue, on_delete=models.CASCADE, related_name='productattributevalues', verbose_name='Attribute values' |
productinventory | productinventory | ForeignKey | ProductInventory, on_delete=models.CASCADE, related_name='productattributevalues', verbose_name='Product inventory' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
product_attribute | product_attribute | ForeignKey | ProductAttribute, on_delete=models.CASCADE, related_name='producttypeattribute', verbose_name='Product attribute' |
product_type | product_type | ForeignKey | ProductType, on_delete=models.CASCADE, related_name='producttypeattribute', verbose_name='Product type' |
The decision to implement unique_together model method was made due to the wider coverage of it rather than UniqueConstraint which has been added in Django 4.0.0. Link to Django Documentation
Name | Database Key | Field Type | Validation |
---|---|---|---|
email_name | email_name | CharField | max_length=100, null=False, unique=True, blank=False, verbose_name='Email name' |
content | content | TextField | null=False, blank=False, verbose_name='Content' |
code | code | CharField | max_length=100, null=True, blank=True, verbose_name='Code' |
created_at | created_at | DateTimeField | auto_now_add=True, verbose_name='Created at' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
user | user | ForeignKey | User, on_delete=models.CASCADE, verbose_name='Requested user' |
requested_product | requested_product | ForeignKey | Product, on_delete=models.CASCADE, verbose_name='Requested product' |
requested_attributes_values | requested_attributes_values | ManyToManyField | ProductAttributeValue, related_name="requested_attributes_values", through="RequestedAttributesValues", verbose_name='Requested attributes values' |
requested_quantity | requested_quantity | PositiveIntegerField | verbose_name='Requested quantity' |
created_at | created_at | DateTimeField | auto_now_add=True, verbose_name='Created at' |
answer_sent | answer_sent | BooleanField | default=False, verbose_name='Answer send' |
"""Model for Order."""
PENDING = 'Pending'
PROCESSING = 'Processing'
SHIPPED = 'shipped'
COMPLETED = 'Completed'
REFUNDED = 'Refunded'
STATUS_CHOICES = (
(PENDING, 'Pending'),
(PROCESSING, 'Processing'),
(SHIPPED, 'Shipped'),
(COMPLETED, 'Completed'),
(REFUNDED, 'Refunded'),
)
Name | Database Key | Field Type | Validation |
---|---|---|---|
user | user | ForeignKey | User, on_delete=models.CASCADE, verbose_name='Requested user' |
full_name | full_name | CharField | max_length=50, null=False, blank=False, verbose_name='Full name' |
CharField | max_length=50, null=False, blank=False, verbose_name='Email' | ||
phone | phone | CharField | max_length=100, null=False, blank=False, verbose_name='Phone' |
address1 | address1 | CharField | max_length=250, null=False, blank=False, verbose_name='Address1' |
address2 | address2 | CharField | max_length=250, null=False, blank=False, verbose_name='Address2' |
city | city | CharField | max_length=100, null=False, blank=False, verbose_name='City' |
county_region_state | county_region_state | CharField | max_length=100, null=False, blank=False, verbose_name='County/Region/State' |
country | country | CharField | max_length=100, null=False, blank=False, verbose_name='Country' |
zip_code | zip_code | CharField | max_length=20, null=False, blank=False, verbose_name='Zip code' |
created | created | DateTimeField | auto_now_add=True, verbose_name='Created' |
updated | updated | DateTimeField | auto_now=True, verbose_name='Updated' |
total_paid | total_paid | DecimalField | max_digits=5, decimal_places=2, null=False, blank=False, verbose_name='Total paid' |
order_number | order_number | CharField | max_length=32, null=False, editable=False, verbose_name='Order number' |
order_key | order_key | CharField | max_length=200, blank=True, null=True, verbose_name='Order key' |
billing_status | billing_status | BooleanField | default=False, verbose_name='Billing status' |
status | status | CharField | max_length=20, choices=STATUS_CHOICES, default=PENDING, verbose_name='Status' |
The decision to implement several options for the order identification numbers was due to the variety of the possibilities of the identification numbers in different countries.
Name | Database Key | Field Type | Validation |
---|---|---|---|
order | order | ForeignKey | Order, on_delete=models.CASCADE, related_name='order_item', verbose_name='Order' |
product_inventory | product_inventory | ForeignKey | ProductInventory, on_delete=models.CASCADE, related_name='order_item_inventory', verbose_name='Product inventory' |
quantity | quantity | PositiveIntegerField | verbose_name='Quantity' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
name | name | CharField | max_length=100, unique=True, blank=False, null=False, verbose_name='Name' |
slug | slug | SlugField | max_length=100, unique=True, blank=False, null=False, verbose_name='Slug' |
description | description | TextField | null=False, blank=False, verbose_name='Description' |
promotion_code | promotion_code | CharField | max_length=100, unique=True, blank=False, null=False, verbose_name='Promotion code' |
promotion_reduction | promotion_reduction | DecimalField | max_digits=3, decimal_places=0, default=Decimal(0), validators=PERCENTAGE_VALIDATOR, verbose_name='Promotion reduction' |
active | active | BooleanField | default=True, verbose_name='Active' |
start_date | start_date | DateTimeField | null=False, blank=False, verbose_name='Start date' |
end_date | end_date | DateTimeField | null=False, blank=False, verbose_name='End date' |
products_inventory_in_promotion | products_inventory_in_promotion | ManyToManyField | blank=True, related_name='products_promotions', verbose_name='Products inventory in promotion' |
created_at | created_at | DateTimeField | auto_now_add=True, verbose_name='Created at', help_text='Date and time of creation.' |
updated_at | updated_at | DateTimeField | auto_now=True, verbose_name='Updated at', help_text='Date and time of last update.' |
STAR_CHOICES = (
('1', '1'),
('2', '2'),
('3', '3'),
('4', '4'),
('5', '5'),
)
Name | Database Key | Field Type | Validation |
---|---|---|---|
user | user | ForeignKey | User, on_delete=models.CASCADE, related_name='reviews', verbose_name='User' |
product | product | ForeignKey | Product, on_delete=models.CASCADE, related_name='reviews', verbose_name='Product' |
order | order | ForeignKey | Order, on_delete=models.CASCADE, related_name='reviews', verbose_name='Order' |
rating | rating | CharField | max_length=20, choices=STAR_CHOICES, default=1, verbose_name='Rating' |
comment | comment | TextField | max_length=1000, blank=True, null=True, verbose_name='Comment' |
created_at | created_at | DateTimeField | auto_now_add=True, verbose_name='Created at', help_text='Date and time of creation.' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
review | review | ForeignKey | Review, on_delete=models.CASCADE, related_name='images', verbose_name='Review' |
image | image | CloudinaryField | null=True, blank=True, verbose_name='Image' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
company_name | company_name | CharField | max_length=100, unique=True, blank=False, null=False, verbose_name='Company name' |
company_email | company_email | EmailField | max_length=100, unique=True, blank=False, null=False, verbose_name='Company email' |
company_phone | company_phone | CharField | max_length=20, unique=True, blank=False, null=False, verbose_name='Company phone' |
company_contact_name | company_contact_name | CharField | max_length=100, unique=True, blank=False, null=False, verbose_name='Company contact name' |
country | country | CharField | max_length=100, unique=True, blank=False, null=False, verbose_name='Country' |
city | city | CharField | max_length=100, unique=True, blank=False, null=False, verbose_name='City' |
state_region_county | state_region_county | CharField | max_length=100, unique=True, blank=False, null=False, verbose_name='State/region/county' |
zip_code | zip_code | CharField | max_length=20, unique=True, blank=False, null=False, verbose_name='Zip code' |
address | address | CharField | max_length=100, unique=True, blank=False, null=False, verbose_name='Address' |
notes | notes | TextField | max_length=1000, blank=True, null=True, verbose_name='Notes' |
created_at | created_at | DateTimeField | auto_now_add=True, verbose_name='Created at', help_text='Date and time of creation.' |
updated_at | updated_at | DateTimeField | auto_now=True, verbose_name='Updated at', help_text='Date and time of last update.' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
product_inventory | product_inventory | ForeignKey | ProductInventory, on_delete=models.CASCADE, related_name='store_purchases', verbose_name='Product inventory' |
units | units | PositiveIntegerField | verbose_name='Units' |
total_spending | total_spending | DecimalField | max_digits=5, decimal_places=2, null=False, blank=False, verbose_name='Total spending' |
supplier | supplier | ForeignKey | Supplier, on_delete=models.CASCADE, related_name='store_purchases', verbose_name='Supplier' |
delivered | delivered | BooleanField | default=False, verbose_name='Delivered' |
created_at | created_at | DateTimeField | auto_now_add=True, verbose_name='Created at', help_text='Date and time of creation.' |
updated_at | updated_at | DateTimeField | auto_now=True, verbose_name='Updated at', help_text='Date and time of last update.' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
members | members | ManyToManyField | blank=True, related_name='live_support_chats', verbose_name='Members' |
created_at | created_at | DateTimeField | auto_now_add=True, verbose_name='Created at', help_text='Date and time of creation.' |
updated_at | updated_at | DateTimeField | auto_now=True, verbose_name='Updated at', help_text='Date and time of last update.' |
Name | Database Key | Field Type | Validation |
---|---|---|---|
chat | chat | ForeignKey | LiveSupportChat, on_delete=models.CASCADE, related_name='messages', verbose_name='Chat' |
author | author | ForeignKey | User, on_delete=models.CASCADE, related_name='live_support_messages', verbose_name='Author' |
content | content | TextField | max_length=1000, blank=True, null=True, verbose_name='Content' |
image | image | CloudinaryField | null=True, blank=True, verbose_name='Image' |
created_at | created_at | DateTimeField | auto_now_add=True, verbose_name='Created at', help_text='Date and time of creation.' |
updated_at | updated_at | DateTimeField | auto_now=True, verbose_name='Updated at', help_text='Date and time of last update.' |
Please refer to the TESTING.md file for all test-related documentation.
-
The app was deployed to Render.
-
The database was deployed to ElephantSQL.
-
The app can be reached by the link.
Please refer to the DEPLOYMENT.md file for all deployment and payment-related documentation.
- GitHub for giving the idea of the project's design.
- Django for the framework.
- Font awesome: for the free access to icons.
- Render: for providing a free hosting.
- jQuery: for providing varieties of tools to make standard HTML code look appealing.
- jQuery UI: for providing varieties of tools to make standard HTML code look appealing.
- Postgresql: for providing a free database.
- geonames: for providing a free database on countries, regions, cities.
- Multiple Video & Image Upload Plugin - jQuery Miv.js: for providing a free plugin to upload multiple videos and images.
- Stripe: for providing a free payment gateway.
- htmlcolorcodes.com: for providing a free database on colors.
- Very Academy Youtube Channel: for brilliant tutorials, which shed the light on the implementation of database with multi-values products, precise explanations of the stripe API, and many other things!
- birme: for providing free service to center and crop images.
- fontawesome: for providing free icons.
- googlefonts: for providing free fonts.
- BGJar: for the free access to the background images build tool.
- Responsive Viewer: for providing a free platform to test website responsiveness
- GoFullPage: for allowing to create free full web page screenshots;
- Favicon Generator. For real.: for providing a free platform to generate favicons.
- Sitemap Generator: for providing a free platform to generate sitemaps.
- Coolors: for providing a free platform to generate your own palette.
- Elon Musk: for providing a template for the twitter mock-up page;
- unsplash: for providing a free products' images.
- Icons8: for providing free access to amazing icons and illustrations to fill out the store.
- unsplash: for providing free products' images to fill out the store.
- chrome developer tools: for providing a free platform to test website.
- adidas: for providing free products' data and images to fill out the store on clothes and shoes.
- fashionunited: for providing content for the newsletter;
- dell: for providing free products' data and images to fill out the store on computers and laptops.
- nike: for providing free products' data and images to fill out the store on clothes and shoes.
- artsaber: for providing free products' images to fill out the store on lightsabers data and images.
- backwaterreptiles: for providing free products' images to fill out the store on tarantulas' data and images.
- Yum Of China: for providing free data on Chinese beer.
- lego: for providing free products' data and images to fill out the store with toys.
- maggie: for providing free products' data and images to fill out the store with maggie products.
- barilla: for providing free products' data and images to fill out the store with pasta.
- LG electronics: for providing free products' data and images to fill out the store with electronics.
- Tim Nelson was a great supporter of another bold idea of mine for this project. Tim guided me through the development of the project and helped me to learn a lot of new things by challenging me to do something new.
- Aleksei Konovalov, my husband and coding partner, assisted me greatly in product values js selection control implementation and helped me to stay sane.
- Very Academy Youtube Channel provided great insight on the implementation of the database with multi-values products, precise explanations of the stripe API, and many other things! This Youtube channel has plenty of brilliant tutorials that shed light on Django's most curious and useful aspects.