Quotation
Quotations are purchase offers that occur after a basket submission. The user wants to create a quotation from the products in their basket and can create a new quotation by giving a name.
If the division of the user is main-division then the initial status of quotation will be set to pending status. If the division of the user is sub-division the initial status of quotation depends on IS_SUB_DIVISION_QUOTATION_APPROVAL_REQUIRED
dynamic setting. If it is true the status will be set to request approval, if it is false the status will be set to pending status. The default value of this setting is true.
There is a hierarchy regarding updating the quotation. You can understand this structure better in the attached image.
How to Manage Quotations?
There are 2 API services to manage quotations:
Via omnishop platform /api/v1/my-quotations/
endpoint to manage operations for users to their own quotations. Via omnitron admin users can use /api/v1/quotations/
endpoint to manage operations on all quotations.
Both parts perform similar operations. Let's consider both separately. First, let's look at the requests that are sent via commerce. Requests to the B2B project via Commerce are sent to /api/v1/my-quotations/.
List Quotations
Example Request
curl --location 'http://{B2B_URL}/api/v1/my-quotations/' \
--header 'Authorization: Token {token}'
Example Response (200 OK)
{
"count": 1,
"next": "",
"previous": null,
"results": [
{
"id": 89,
"number": "351284518745",
"total_amount": "69.97",
"net_amount": "69.97",
"name": "Test",
"user": {
"id": 268,
"first_name": "first",
"last_name": "last",
"email": "akinon@akinon.com",
"division": {
"id": 1,
"name": "Akinon Main Division",
"phone_number": "02123332211",
"erp_code": "AKI01"
}
},
"created_at": "2023-11-30T13:34:44.520172Z",
"modified_at": "2023-12-04T17:46:00.668207Z",
"status": "cancelled"
}
]
}
Users can filter quotations by name, user, status, total_amount, created_at, modified_at, division and phone_number. For example:
curl --location '{B2B_Backend_URL}/api/v1/my-quotations/?status=cancelled' \
--header 'Authorization: Token {token}'
List Quotations Detail
There are eight different statuses for quotations. These are cancelled, request_approval, pending, draft, sending_approval_request, approved, rejected and expired.
- Cancelled: The quotation has been canceled by the owner.
- Request Approval: Created by a sub-division, awaiting approval from the main division.
- Pending: Approved by the main division or created by a division, awaiting approval from the superuser.
- Draft: Used by the superuser to edit the quotation.
- Sending Approval Request: Utilized by the superuser to send the quotation to the owner for approval.
- Approved: Successfully approved by the superuser and is ready for ordering.
- Rejected: Rejected either by the superuser or the main division.
- Expired: The quotation has expired.
Example Request
curl --location '{B2B_Backend_URL}/api/v1/my-quotations/<quotation.id>/' \
--header 'Authorization: Token {token}'
Example Response (200 OK)
{
"id": 88,
"number": "257932207177",
"total_amount": "39.98",
"net_amount": "39.98",
"name": "segment quotation test",
"user": {
"id": 2,
"first_name": "first",
"last_name": "last",
"email": "akinon@akinon.com",
"division": {
"id": 1,
"name": "Akinon Main Division",
"phone_number": "02123332211",
"erp_code": "AKI01"
}
},
"created_at": "2023-11-29T11:38:52.208717Z",
"modified_at": "2023-11-29T11:39:09.283115Z",
"status": "approved",
"quotation_items": [
{
"product_remote_id": 1,
"quantity": 2,
"price": "19.99",
"total_amount": "39.98",
"product": {
"sku": "dummy01",
"name": "dummy",
"price": "19.99",
"asorti": "1573772",
"category": "Şapka",
"currency": "try",
"variants": {},
"product_image": "https://<address>/file.jpg"
},
"divisions": [
{
"id": 1,
"name": "Akinon Main Division",
"address": "221B Baker Street, London center. k",
"erp_code": "AKI01",
"quantity": 2
}
]
}
],
"discount": null,
"expiration_date": "2023-12-14T11:38:52.208517Z",
"segment_remote_id": 3,
"comments": [
{
"id": 291,
"user": {
"id": 38,
"first_name": "first",
"last_name": "last",
"email": "akinon@akinon.com",
"division": {
"id": 1,
"name": "Akinon Main Division",
"phone_number": "02123332211",
"erp_code": "AKI01"
}
},
"comment_type": "approval",
"text": "it is ok.",
"created_at": "2023-12-05T14:56:06.771530Z",
"modified_at": "2023-12-05T14:56:06.771556Z"
},
]
}
Create Quotation
Before creating a quotation, users have to add products to their basket.
Example Request
curl --location '{B2B_Backend_URL}/api/v1/my-quotations/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Token {token}' \
--data '{
"name": "Quotation 1"
}'
Example Response (200 OK)
{
"name": "Quotation 1"
}
Cancel Quotation
After the create quotation users can cancel & reject their quotation from the my-quotations endpoint but can not approve. Because firstly, the quotation should be sent to the divisions for approval. Once the quotation is approved, it is sent back to the quotation owners for their approval. Following their approval, the quotation can then be converted into orders.
Example Request
curl --location '{B2B_Backend_URL}/api/v1/my-quotations/<quotation.id>/cancel/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Token {token}' \
--data '{
"reason": "I dont want to do it"
}'
Reject Quotation
In order to reject a quotation, you must be the owner of the quotation or be affiliated with the main-division.
Example Request
curl --location '{B2B_Backend_URL}/api/v1/my-quotations/<quotation.id>/reject/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Token {token}' \
--data '{
"reason": "I dont want to do it"
}'
To see the comments of a quotation use the following API:
curl --location 'http://{B2B_Backend_URL}/api/v1/my-quotations/<quotation.id>/comments/' \
--header 'Authorization: Token {token}'
To Add comment for a quotation use the following API:
curl --location 'http://{B2B_Backend_URL}/api/v1/my-quotations/<quotation.id>/comments/' \
--header 'Authorization: Token {token}'
Approve Pending Request Approval Quotation
Approve for Pending Quotation use the following API: Requests created by sub-division users wait as request_approval before becoming requests as status. In this section, request_approval is used to approve the pending requests and put them on pending. If the quotation's status is set to request_approval and the creator user is affiliated with the main division, one can approve the quotation. If the quotation status is different or the user's division type is not main, the request will not be successful.
Example Request
curl --location --request POST '{B2B_Backend_URL}/api/v1/my-quotations/<quotation.id>/approve_pending/' \
--header 'Authorization: Token {token}'
Approve Quotation
Requests created by the sub-division users pass to the "sending_request_approval" status after they are approved by the parent dealer. Here it waits for the bidder's approval.
Example Request
curl --location '{B2B_Backend_URL}/api/v1/my-quotations/<quotation.id>/approve/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Token {token}' \
--data '{
"text": "ok"
}'
Admin Operations on Quotations
List Quotations
Example Request
curl --location '{B2B_Backend_URL}/api/v1/quotations/' \
--header 'Authorization: Token {token}'
Users can filter quotations by name, user, status, total_amount, created_at, modified_at, division and phone_number.
curl --location '{B2B_Backend_URL}/api/v1/quotations/?number=593075984145' \
--header 'Authorization: Token {token}'
List Quotations Detail
There are eight different statuses for quotations. These are cancelled, request_approval, pending, draft, sending_approval_request, approved, rejected and expired.
- Cancelled: The quotation has been canceled by the owner.
- Request Approval: Created by a sub-division, awaiting approval from the main division.
- Pending: Approved by the main division or created by a division, awaiting approval from the superuser.
- Draft: Used by the superuser to edit the quotation.
- Sending Approval Request: Utilized by the superuser to send the quotation to the owner for approval.
- Approved: Successfully approved by the superuser and is ready for ordering.
- Rejected: Rejected either by the superuser or the main division.
- Expired: The quotation has expired.
Example Request
curl --location '{B2B_Backend_URL}/api/v1/quotations/<quotation.id>/' \
--header 'Authorization: Token {token}'
List Quotation Comments
Example Request
curl --location '{B2B_Backend_URL}/api/v1/quotations/<quotation.id>/comments/' \
--header 'Authorization: Token {token}'
Example Response (200 OK)
[
{
"id": 288,
"user": {
"id": 268,
"first_name": "first",
"last_name": "last",
"email": "akinon@akinon.com",
"division": {
"id": 1,
"name": "Akinon Main Division",
"phone_number": "02123332211",
"erp_code": "AKI01"
}
},
"comment_type": "rejection",
"text": "text for rejection",
"created_at": "2023-12-05T11:55:54.175573Z",
"modified_at": "2023-12-05T11:55:54.175593Z"
}
]
Add Product to Quotation
Example Request
curl --location '{B2B_Backend_URL}/api/v1/quotations/<quotation.id>/add_product_by_sku/' \
--header 'Authorization: Token {token}'
--header 'Content-Type: application/json' \
--data '{
"division": 2,
"quantity": 1,
"sku": "dummy01"
}'
Example Response (200 OK)
{
"id": 76,
"number": "63779097837",
"total_amount": "389.83",
"net_amount": "369.84",
"name": "without segment id",
"user": {
"id": 38,
"first_name": "first",
"last_name": "last",
"email": "akinon@akinon.com",
"division": {
"id": 1,
"name": "Akinon Main Division",
"phone_number": "02123332211",
"erp_code": "AKI01"
}
},
"created_at": "2023-11-22T07:23:10.979832Z",
"modified_at": "2023-12-05T14:41:34.014032Z",
"status": "draft",
"quotation_items": [
{
"product_remote_id": 1,
"quantity": 16,
"price": "19.99",
"total_amount": "319.84",
"product": {
"sku": "dummy01",
"name": "dummy",
"price": "19.99",
"asorti": "1573772",
"category": "Şapka",
"currency": "try",
"variants": {},
"product_image": "https://file.jpg"
},
"divisions": [
{
"id": 2,
"name": "SADA",
"address": "Sada",
"erp_code": "213213",
"quantity": 1
},
{
"id": 1,
"name": "Akinon Main Division",
"address": "221B Baker Street, London center. k",
"erp_code": "AKI01",
"quantity": 16
}
]
},
],
"discount": null,
"expiration_date": "2023-12-07T07:23:10.979628Z",
"segment_remote_id": null
}
Add Comments to Quotation
Example Request
curl --location '{B2B_Backend_URL}/api/v1/quotations/<quotation.id>/comments/' \
--header 'Authorization: Token {token}'
--header 'Content-Type: application/json' \
--data '{
"text": "Comment 1"
}'
Example Response (200 OK)
{
"id": 290,
"user": {
"id": 38,
"first_name": "first",
"last_name": "last",
"email": "akinon@akinon.com",
"division": {
"id": 1,
"name": "Akinon Main Division",
"phone_number": "02123332211",
"erp_code": "AKI01"
}
},
"comment_type": "conversation",
"text": "Comment 1",
"created_at": "2023-12-05T14:45:26.421232Z",
"modified_at": "2023-12-05T14:45:26.421253Z"
}
Create Discount for Quotation
There are two different discount types, percentage and fixed. percentage is used to define a 'percentage' discount on the price. Used 'fixed' when you want to apply a fixed price discount.
Example Request
curl --location '{B2B_Backend_URL}/api/v1/quotations/<quotation.id>/discount/' \
--header 'Authorization: Token {token}'
--header 'Content-Type: application/json' \
--data '{
"value": "10",
"discount_type": "percentage" # or "fixed"
}'
Example Response (200 OK)
{
"id": 298,
"value": "10.00",
"discount_type": "percentage",
"discount_amount": "38.98",
"net_amount": "350.85",
"created_at": "2023-12-05T14:47:09.059855Z",
"modified_at": "2023-12-05T14:47:09.059889Z"
}
Delete Discount for Quotation
Example Request
curl --location --request DELETE '{B2B_Backend_URL}/api/v1/quotations/<quotation.id>/discount/' \
--header 'Authorization: Token {token}' \
--header 'Content-Type: application/json' \
--data '{
"value": "12",
"discount_type": "percentage"
}'
Update Quotation
Example Request
curl --location --request PUT '{B2B_Backend_URL}/api/v1/quotations/<quotation.id>/' \
--header 'Authorization: Token {token}'\
--header 'Content-Type: application/json' \
--data '{
"product_remote_id": 1,
"division": 1,
"quantity": 5
}'
Example Response (200 OK)
{
"id": 76,
"number": "63779097837",
"total_amount": "169.94",
"net_amount": "169.94",
"name": "without segment id",
"user": {
"id": 38,
"first_name": "first",
"last_name": "last",
"email": "akinon@akinon.com",
"division": {
"id": 1,
"name": "Akinon Main Division",
"phone_number": "02123332211",
"erp_code": "AKI01"
}
},
"created_at": "2023-11-22T07:23:10.979832Z",
"modified_at": "2023-12-05T14:53:29.425783Z",
"status": "draft",
"quotation_items": [
{
"product_remote_id": 1,
"quantity": 1,
"price": "19.99",
"total_amount": "19.99",
"product": {
"sku": "dummy01",
"name": "dummy",
"price": "19.99",
"category": "Şapka",
"currency": "try",
"variants": {},
"base_code": "1573772",
"product_image": "https://file.jpg"
},
"divisions": [
{
"id": 2,
"name": "SADA",
"address": "ASDASDA asd as da sd as das asdS",
"erp_code": "213213",
"quantity": 1
},
{
"id": 1,
"name": "Akinon Main Division",
"address": "221B Baker Street, London center. k",
"erp_code": "AKI01",
"quantity": 5
}
]
}
],
"discount": null,
"expiration_date": "2023-12-07T07:23:10.979628Z",
"segment_remote_id": null
}
Set Quotation Status as Draft
Example Request
curl --location --request POST '{B2B_Backend_URL}/api/v1/quotations/<quotation.id>/draft/' \
--header 'Authorization: Token {token}'
Approve Quotation
After the quotation is approved, orders are created on the b2b according to the division count in quotation and B2B_BULK_PROCESSES_BATCH_SIZE
environment variable. Let's say that the quotation below is approved and B2B_BULK_PROCESSES_BATCH_SIZE
value is 500.
Product | Division | Quantity |
---|---|---|
Product X | Division A | 600 |
Product Y | Division A | 300 |
Product X | Division B | 300 |
Since the environment value of B2B_BULK_PROCESSES_BATCH_SIZE
is 500, orders are divided into pieces of 500.
Following orders will be created.
Order 1:
Division A − Product X * 500
Order 2:
Division A − Product X * 100 − Product Y * 300
Order 3:
Division B − Product X * 300
Example Request
curl --location '{B2B_Backend_URL}/api/v1/quotations/<quotation.id>/approve/' \
--header 'Authorization: Token {token}'\
--header 'Content-Type: application/json' \
--data '{
"text": "it is ok."
}'
Example Response (200 OK)
{
"id": 76,
"number": "63779097837",
"total_amount": "169.94",
"net_amount": "169.94",
"name": "without segment id",
"user": {
"id": 38,
"first_name": "first",
"last_name": "last",
"email": "akinon@akinon.com",
"division": {
"id": 1,
"name": "Akinon Main Division",
"phone_number": "02123332211",
"erp_code": "AKI01"
}
},
"created_at": "2023-11-22T07:23:10.979832Z",
"modified_at": "2023-12-05T14:56:06.694967Z",
"status": "approved",
"quotation_items": [
{
"product_remote_id": 1,
"quantity": 5,
"price": "19.99",
"total_amount": "99.95",
"product": {
"sku": "dummy01",
"name": "dummy",
"price": "19.99",
"asorti": "1573772",
"category": "Şapka",
"currency": "try",
"variants": {},
"product_image": "https://file.jpg"
},
"divisions": [
{
"id": 2,
"name": "SADA",
"address": "ASDASDA asd as da sd as das asdS",
"erp_code": "213213",
"quantity": 1
},
{
"id": 1,
"name": "Akinon Main Division",
"address": "221B Baker Street, London center. k",
"erp_code": "AKI01",
"quantity": 5
}
]
}
],
"discount": null,
"expiration_date": "2023-12-07T07:23:10.979628Z",
"segment_remote_id": null
}
Get Quotation Orders
There are three status for orders. These are pending, created and failed. Once orders are created, they enter the "pending" status. This means that the Order is waiting to be created as an order on commerce. When the order is created on commerce, the status is updated to “created”. If an error occurs during this process, the "failed" status is used.
Example Request
curl --location '{B2B_Backend_URL}/api/v1/quotations/<quotation.id>/orders/' \
--header 'Authorization: Token {token}''
Example Response (200 OK)
[
{
"id": 206,
"remote_number": "2465661458111961",
"division": {
"id": 2,
"name": "SADA",
"phone_number": "05XXXXXXXXX",
"erp_code": "213213"
},
"order_items": [
{
"id": 174,
"product_remote_id": 1,
"quantity": 1,
"data": {
"sku": "dummy01",
"name": "dummy",
"price": "19.99",
"category": "Şapka",
"currency": "try",
"variants": {},
"base_code": "1573772",
"product_image": "https://file.jpg"
},
"created_at": "2023-12-05T14:56:06.716593Z",
"modified_at": "2023-12-05T14:56:06.716606Z"
}
],
"expected_amount": "19.99",
"expected_net_amount": "19.99",
"discount_amount": "0.00",
"created_at": "2023-12-05T14:56:06.713154Z",
"modified_at": "2023-12-05T14:56:09.057338Z",
"status": "created"
}
]
Reject Quotation
In order to reject a quotation, you must be the owner of the quotation or be affiliated with the main-division.
Example Request
curl --location '{B2B_Backend_URL}/api/v1/quotations/<quotation.id>/reject/' \
--header 'Authorization: Token {token}' \
--header 'Content-Type: application/json' \
--data '{
"reason": "I don'\''t like it."
}'
Send Approval Request to Quotation
Example Request
curl --location --request POST '{B2B_Backend_URL}/api/v1/quotations/<quotation.id>/send_approval_request/' \
--header 'Authorization: Token {token}''
Trigger Order Creation
Example Request
curl --location '{B2B_Backend_URL}/api/v1/quotations/<quotation.id>/trigger_order_creation/' \
--header 'Authorization: Token {token}' \
--header 'Content-Type: application/json' \
--data '{
"order": 76
}'
Export Quotations
Exports existing quotations based on the specified query parameters. Returns a cache key for status tracking.
Request Parameters
Parameter | Description |
---|---|
fields | Specifies the fields to be included in the export file. Available fields: product_remote_id, quantity, price, total_amount, product, divisions |
format | Specifies the file format for the export. Acceptable values are csv and xls . The default is xls . |
status_cache_key | A query parameter to track the status of the file export using the cache key returned from the initial request. |
Example Request
curl --location --request GET '{B2B_Backend_URL}/api/v1/quotations/?fields=name&fields=total_amount' \
--header 'Authorization: Token {token} \
--header 'Content-Type: application/json'
Example Response (200 OK)
{
"cache_key": "beacaa8ba65571e3840e28a11f17481e"
}
Export File Status
Checks the status of an export file using the cache key obtained from the export request. Provides information on whether the file is ready for download.
Example Request
curl --location '{B2B_Backend_URL}/api/v1/quotations/?status_cache_key=beacaa8ba65571e3840e28a11f17481e' \
--header 'Authorization: Token {token}'
Example Response
{
"is_ready": true,
"url": "{url}/export_file/beacaa8ba65571e3840e28a11f17481e.xls",
"status": "completed"
}
Response Parameters
Field | Description |
---|---|
is_ready | Indicates whether the file is ready for download (true or false ). |
url | The URL to download the exported file once it is ready. |
status | Provides the current status of the export process (e.g., waiting, completed ). |