From ec7ea5e1ef5be01d283ee9939c0e7ba60d3d8bf2 Mon Sep 17 00:00:00 2001 From: diPhantxm Date: Sat, 18 Mar 2023 18:16:31 +0300 Subject: [PATCH] add some methods for shipment and delivering for fbs --- ENDPOINTS.md | 6 +- ozon/fbs.go | 200 +++++++++++++++++++++++++++++++++++++++++ ozon/fbs_test.go | 227 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 430 insertions(+), 3 deletions(-) diff --git a/ENDPOINTS.md b/ENDPOINTS.md index 57a9a3e..c973db1 100644 --- a/ENDPOINTS.md +++ b/ENDPOINTS.md @@ -115,11 +115,11 @@ - [ ] Cancel the shipment - [ ] Add weight for bulk products in a shipment - [ ] Cancel sending some products in the shipment -- [ ] List of shipment certificates +- [x] List of shipment certificates - [ ] Sign shipment certificates - [ ] List of shipments in the certificate -- [ ] Change the status to "Delivering" -- [ ] Add tracking numbers +- [x] Change the status to "Delivering" +- [x] Add tracking numbers - [ ] Change the status to "Last Mile" - [ ] Change the status to "Delivered" - [ ] Change status to "Sent by seller" diff --git a/ozon/fbs.go b/ozon/fbs.go index 1bea3ca..3e92713 100644 --- a/ozon/fbs.go +++ b/ozon/fbs.go @@ -812,3 +812,203 @@ func (c FBS) GetShipmentDataByIdentifier(params *GetShipmentDataByIdentifierPara return resp, nil } + +type ChangeStatusToDeliveringParams struct { + // Shipment identifier + PostingNumber []string `json:"posting_number"` +} + +type ChangeStatusToDeliveringResponse struct { + core.CommonResponse + + // Method result + Result []struct { + // Error when processing the request + Error []string `json:"error"` + + // Shipment number + PostingNumber string `json:"posting_number"` + + // If the request is executed without errors — true + Result bool `json:"result"` + } `json:"result"` +} + +// Changes the shipment status to "Delivering" if a third-party delivery service is being used +func (c FBS) ChangeStatusToDelivering(params *ChangeStatusToDeliveringParams) (*ChangeStatusToDeliveringResponse, error) { + url := "/v2/fbs/posting/delivering" + + resp := &ChangeStatusToDeliveringResponse{} + + response, err := c.client.Request(http.MethodPost, url, params, resp) + if err != nil { + return nil, err + } + response.CopyCommonResponse(&resp.CommonResponse) + + return resp, nil +} + +type AddTrackingNumbersParams struct { + // An array with shipment identifier—tracking number pairs + TrackingNumbers []FBSTrackingNumbersParams `json:"tracking_numbers"` +} + +type FBSTrackingNumbersParams struct { + // Shipment identifier + PostingNumber string `json:"posting_number"` + + // Shipment tracking number + TrackingNumber string `json:"tracking_number"` +} + +type AddTrackingNumbersResponse struct { + core.CommonResponse + + // Method result + Result []struct { + // Error when processing the request + Error []string `json:"error"` + + // Shipment number + PostingNumber string `json:"posting_number"` + + // If the request is executed without errors — true + Result bool `json:"result"` + } `json:"result"` +} + +// Add tracking numbers to shipments +func (c FBS) AddTrackingNumbers(params *AddTrackingNumbersParams) (*AddTrackingNumbersResponse, error) { + url := "/v2/fbs/posting/tracking-number/set" + + resp := &AddTrackingNumbersResponse{} + + response, err := c.client.Request(http.MethodPost, url, params, resp) + if err != nil { + return nil, err + } + response.CopyCommonResponse(&resp.CommonResponse) + + return resp, nil +} + +type ListOfShipmentCertificatesParams struct { + // Filter parameters + Filter ListOfShipmentCertificates `json:"filter"` + + // Maximum number of certificates in the response + Limit int64 `json:"limit"` +} + +type ListOfShipmentCertificates struct { + // Initial date of shipment creation + DateFrom string `json:"date_from"` + + // Final date of shipment creation + DateTo string `json:"date_to"` + + // Type of integration with the delivery service: + // - ozon — delivery by the Ozon service, + // - 3pl_tracking — delivery by the integrated service, + // - non_integrated — delivery by a third-party service, + // - aggregator — delivery by Ozon partner delivery + IntegrationType string `json:"integration_type"` + + // Freight statuses + Status []string `json:"status"` +} + +type ListOfShipmentCertificatesResponse struct { + core.CommonResponse + + // Request result + Result []struct { + // Shipment identifier + Id int64 `json:"id"` + + // Delivery method identifier + DeliveryMethodId int64 `json:"delivery_method_id"` + + // Delivery method name + DeliveryMethodName string `json:"delivery_method_name"` + + // Type of integration with the delivery service: + // - ozon — delivery by the Ozon service, + // - 3pl — delivery by the integrated service + IntegrationType string `json:"integration_type"` + + // Number of package units + ContainersCount int32 `json:"container_count"` + + // Shipping status + Status string `json:"status"` + + // Shipping date + DepartureDate string `json:"departure_date"` + + // Shipping record creation date + CreatedAt time.Time `json:"created_at"` + + // Shipping record update date + UpdatedAt time.Time `json:"updated_at"` + + // Acceptance certificate type for FBS sellers + ActType string `json:"act_type"` + + // Indication of a partial freight. true if the freigth is partial. + // + // Partial freigt means that the shipment was splitted into several parts + // and for each of them you need to generate separate acts + IsPartial bool `json:"is_partial"` + + // Indication that there are shipments subject to shipping that are not in the current freight. + // true if there are such shipments + HasPostingsForNextCarriage bool `json:"has_postings_for_next_carriage"` + + // Serial number of the partial freight + PartialNum int64 `json:"partial_num"` + + // Information about shipment certificates + RelatedDocs struct { + // Information about acceptance certificate + ActOfAcceptance FBSAct `json:"act_of_acceptance"` + + // Information about discrepancy certificate + ActOfMismatch FBSAct `json:"act_of_mismatch"` + + // Information about surplus certificate + ActOfExcess FBSAct `json:"act_of_excess"` + } `json:"related_docs"` + } `json:"result"` +} + +type FBSAct struct { + // Certificate creation date + CreatedAt time.Time `json:"created_at"` + + // Certificate status: + // - FORMING — not ready yet + // - FORMED — formed + // - CONFIRMED — signed by Ozon + // - CONFIRMED_WITH_MISMATCH — signed with discrepancies by Ozon + // - ACCEPTED_BY_CARGO_PLACES — accepted by package units + // - PRINTED_CARRIAGE — digital signature not required + // - ERROR, UNKNOWN_ERROR — error + DocumentStatus string `json:"document_status"` +} + +// Returns a list of shipment certificates allowing to filter them by time period, status, and integration type +func (c FBS) ListOfShipmentCertificates(params *ListOfShipmentCertificatesParams) (*ListOfShipmentCertificatesResponse, error) { + url := "/v2/posting/fbs/act/list" + + resp := &ListOfShipmentCertificatesResponse{} + + response, err := c.client.Request(http.MethodPost, url, params, resp) + if err != nil { + return nil, err + } + response.CopyCommonResponse(&resp.CommonResponse) + + return resp, nil +} diff --git a/ozon/fbs_test.go b/ozon/fbs_test.go index 7dbf551..e4cf942 100644 --- a/ozon/fbs_test.go +++ b/ozon/fbs_test.go @@ -667,3 +667,230 @@ func TestGetShipmentDataByIdentifier(t *testing.T) { } } } + +func TestChangeStatusToDelivering(t *testing.T) { + t.Parallel() + + tests := []struct { + statusCode int + headers map[string]string + params *ChangeStatusToDeliveringParams + response string + }{ + // Test Ok + { + http.StatusOK, + map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"}, + &ChangeStatusToDeliveringParams{ + PostingNumber: []string{"33920157-0018-1"}, + }, + `{ + "result": [ + { + "error": [], + "posting_number": "33920157-0018-1", + "result": true + } + ] + }`, + }, + // Test No Client-Id or Api-Key + { + http.StatusUnauthorized, + map[string]string{}, + &ChangeStatusToDeliveringParams{}, + `{ + "code": 16, + "message": "Client-Id and Api-Key headers are required" + }`, + }, + } + + for _, test := range tests { + c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers)) + + resp, err := c.FBS().ChangeStatusToDelivering(test.params) + if err != nil { + t.Error(err) + } + + if resp.StatusCode != test.statusCode { + t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode) + } + + if resp.StatusCode == http.StatusOK { + if len(resp.Result) != len(test.params.PostingNumber) { + t.Errorf("Length of posting numbers in reqeust and response are not equal") + } + if len(resp.Result) > 0 { + if resp.Result[0].PostingNumber != test.params.PostingNumber[0] { + t.Errorf("Posting numbers in request and response are not equal") + } + } + } + } +} + +func TestAddTrackingNumbers(t *testing.T) { + t.Parallel() + + tests := []struct { + statusCode int + headers map[string]string + params *AddTrackingNumbersParams + response string + }{ + // Test Ok + { + http.StatusOK, + map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"}, + &AddTrackingNumbersParams{ + TrackingNumbers: []FBSTrackingNumbersParams{ + { + PostingNumber: "48173252-0033-2", + TrackingNumber: "123123123", + }, + }, + }, + `{ + "result": [ + { + "error": [], + "posting_number": "48173252-0033-2", + "result": true + } + ] + }`, + }, + // Test No Client-Id or Api-Key + { + http.StatusUnauthorized, + map[string]string{}, + &AddTrackingNumbersParams{}, + `{ + "code": 16, + "message": "Client-Id and Api-Key headers are required" + }`, + }, + } + + for _, test := range tests { + c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers)) + + resp, err := c.FBS().AddTrackingNumbers(test.params) + if err != nil { + t.Error(err) + } + + if resp.StatusCode != test.statusCode { + t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode) + } + + if resp.StatusCode == http.StatusOK { + if len(resp.Result) != len(test.params.TrackingNumbers) { + t.Errorf("Length of tracking numbers in reqeust and response are not equal") + } + if len(resp.Result) > 0 { + if resp.Result[0].PostingNumber != test.params.TrackingNumbers[0].PostingNumber { + t.Errorf("Posting numbers in request and response are not equal") + } + } + } + } +} + +func TestListOfShipmentCertificates(t *testing.T) { + t.Parallel() + + tests := []struct { + statusCode int + headers map[string]string + params *ListOfShipmentCertificatesParams + response string + }{ + // Test Ok + { + http.StatusOK, + map[string]string{"Client-Id": "my-client-id", "Api-Key": "my-api-key"}, + &ListOfShipmentCertificatesParams{ + Limit: 100, + Filter: ListOfShipmentCertificates{ + DateFrom: "2021-08-04", + DateTo: "2022-08-04", + IntegrationType: "ozon", + Status: []string{"delivered"}, + }, + }, + `{ + "result": [ + { + "id": 1234, + "delivery_method_id": 1234, + "delivery_method_name": "string", + "integration_type": "string", + "containers_count": 2, + "status": "string", + "departure_date": "string", + "created_at": "2019-08-24T14:15:22Z", + "updated_at": "2019-08-24T14:15:22Z", + "act_type": "string", + "is_partial": true, + "has_postings_for_next_carriage": true, + "partial_num": 0, + "related_docs": { + "act_of_acceptance": { + "created_at": "2019-08-24T14:15:22Z", + "document_status": "string" + }, + "act_of_mismatch": { + "created_at": "2019-08-24T14:15:22Z", + "document_status": "string" + }, + "act_of_excess": { + "created_at": "2019-08-24T14:15:22Z", + "document_status": "string" + } + } + } + ] + }`, + }, + // Test No Client-Id or Api-Key + { + http.StatusUnauthorized, + map[string]string{}, + &ListOfShipmentCertificatesParams{}, + `{ + "code": 16, + "message": "Client-Id and Api-Key headers are required" + }`, + }, + } + + for _, test := range tests { + c := NewMockClient(core.NewMockHttpHandler(test.statusCode, test.response, test.headers)) + + resp, err := c.FBS().ListOfShipmentCertificates(test.params) + if err != nil { + t.Error(err) + } + + if resp.StatusCode != test.statusCode { + t.Errorf("got wrong status code: got: %d, expected: %d", resp.StatusCode, test.statusCode) + } + + if resp.StatusCode == http.StatusOK { + if len(resp.Result) > 0 { + if resp.Result[0].Id == 0 { + t.Errorf("Id cannot be 0") + } + if resp.Result[0].Status == "" { + t.Errorf("Status cannot be empty") + } + if resp.Result[0].DeliveryMethodId == 0 { + t.Errorf("Delivery method id cannot be 0") + } + } + } + } +}