using System; using Newtonsoft.Json; // ******************************************************************************** // The following code uses the IDE environment: VS2019 and C# 8.0 or later. // Import 'TSNETApiModels.dll' and 'Newtonsoft.Json.dll'(Version: 11.0.1 or later) into your project first.( .Net Framework 4.8 or later) // Then can use model classes with namespace 'TSNETDataInterface.Models'. // ******************************************************************************** namespace TSNETDataInterface.Models { public static class DemoClass { internal static string HowToMakeRequest() { // Make a request: Example for creating a sales order // Type 'OrderSubmission' can be changed to 'PostData', 'PostData1', 'Product' etc. according to different businesses. var oRqt = MakeSignedApiRequest(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here", new OrderSubmission { branch_id = 1, customer_id = 2, warehouse_id = 3, invoice_type = 3003, consignee_name = "Consignee name", consignee_mobile = "Consignee mobile", shipping_address = "Shipping address", /* You can continue to set other fields as needed ... */ carts = new ShopCart[] { new ShopCart() { product_id = 1234, quantity = 10 }, new ShopCart() { product_id = 23456, quantity = 9 }, new ShopCart() { product_id = 56789, quantity = 8 }, /* You can continue to add more items as needed ... */ }, }); // **************************************** // Example for getting paged data resources // **************************************** //var oRqt_Exp1 = MakeSignedApiRequest(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here", // new PostData1 // { // page_number = 1, // page_size = 15, // include_details = false, // timestamp = HowToMakeTimestampInDataOfRequest(), // }); // **************************************** // Example for add a product // **************************************** //var oRqt_Exp2 = MakeSignedApiRequest(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here", // new Product // { // name = "Product name", // code = "Product code", // barcode = "Product barcode", // category_id = 1, // brand_id = 2, // retail_price = 100.000000m, // wholesale_price = 87.654321m, // /* You can continue to set other fields as needed ... */ // }); // The serialized string is put into the request body and sent to the server. string request_body = oRqt.ToJson(); return request_body; } internal static void HowToHandleResponse(string responseBody) { // Example for handle the response after created a sales order // Type 'SalesOrder' can be changed to 'Customer', 'Product', 'Inventory[]' etc. according to different businesses. var oRsp = responseBody.ParseAndVerifyApiResponse(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here"); // **************************************** // More examples. // **************************************** //var oRsp_Exp2 = responseBody.ParseAndVerifyApiResponse(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here"); //var oRsp_Exp3 = responseBody.ParseAndVerifyApiResponse(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here"); //var oRsp_Exp4 = responseBody.ParseAndVerifyApiResponse(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here"); //var oRsp_Exp5 = responseBody.ParseAndVerifyApiResponse>(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here"); // Success if (oRsp != null && oRsp.result_code == ErrorCode.None) { // Put your business code here ... } // Failed else { } } // **************************************** // The following entity classes used are only supported in the model class library in version 2.0.0 or later. // **************************************** /// /// API: POST /api/sales_order_statistics/review /// /// internal static string ExampleRequestForReviewSalesOrderStatistic() { // Make a request of review 'SalesOrderStatistic'. var oRqt = MakeSignedApiRequest(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here", new SalesOrderStatistic { /* The 4 fields 'id', 'branch_id', 'customer_id', and 'review_operator_id' are required */ id = 2, branch_id = 1, customer_id = 11006, review_operator_id = 1 /* Must be 1 */, /* The fields listed below can be filled in according to the fixed values in the example */ invoice_type = 3001, consignee_mobile = "012345", handler_id = 1, }); // The serialized string is put into the request body and sent to the server. string request_body = oRqt.ToJson(); return request_body; } /// /// API: POST /api/sales_order_statistics/review?antis=true /// /// internal static string ExampleRequestForAntiReviewSalesOrderStatistic() { // Make a request of anti-review 'SalesOrderStatistic'. var oRqt = MakeSignedApiRequest(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here", new SalesOrderStatistic { /* The 4 fields 'id', 'branch_id', 'customer_id', and 'review_operator_id' are required */ id = 2, branch_id = 1, customer_id = 11006, review_operator_id = 0 /* Must be 0 */, /* The fields listed below can be filled in according to the fixed values in the example */ invoice_type = 3001, consignee_mobile = "012345", handler_id = 1, }); // The serialized string is put into the request body and sent to the server. string request_body = oRqt.ToJson(); return request_body; } /// /// API: POST /api/sales_order_statistics/change /// /// internal static string ExampleRequestForChangeSalesOrderFlowStatus() { // Make a request of change 'SalesOrderStatistic' flow status. var oRqt = MakeSignedApiRequest(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here", new SalesOrderStatistic { /* The 4 fields 'id', 'branch_id', 'customer_id', and 'flow_status' are required */ id = 2, branch_id = 1, customer_id = 11006, // Flow status: // Confirm repceipt(value is 2), // Cancel payment operation(value is -2) // Start picking up(value is 3), // Picking up finished(value is 4), // Pickup cancellation operation(value is -4) // Start packaging(value is 5), // Packaging finished(value is 6), // Packaging cancellation operation(value is -6) // Shipped(value is 7) // Shipment cancellation operation(value is -7) flow_status = 4, /* The fields listed below can be filled in according to the fixed values in the example */ invoice_type = 3001, consignee_mobile = "012345", handler_id = 1, }); // The serialized string is put into the request body and sent to the server. string request_body = oRqt.ToJson(); return request_body; } /// /// API: POST /api/sales_orders/review /// /// internal static string ExampleRequestForReviewSalesOrder() { // Make a request of review 'SalesOrder'. var oRqt = MakeSignedApiRequest(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here", new SalesOrder { /* The 5 fields 'id', 'branch_id', 'warehouse_id', 'customer_id', and 'review_operator_id' are required */ id = 2, branch_id = 1, warehouse_id = 1, customer_id = 11006, review_operator_id = 1 /* Must be 1 */, }); // The serialized string is put into the request body and sent to the server. string request_body = oRqt.ToJson(); return request_body; } /// /// API: POST /api/sales_orders/review?antis=true /// /// internal static string ExampleRequestForAntiReviewSalesOrder() { // Make a request of anti-review 'SalesOrder'. var oRqt = MakeSignedApiRequest(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here", new SalesOrder { /* The 5 fields 'id', 'branch_id', 'warehouse_id', 'customer_id', and 'review_operator_id' are required */ id = 2, branch_id = 1, warehouse_id = 1, customer_id = 11006, review_operator_id = 0 /* Must be 0 */, }); // The serialized string is put into the request body and sent to the server. string request_body = oRqt.ToJson(); return request_body; } /// /// API: POST /api/warehouse_transfers/review /// /// internal static string ExampleRequestForReviewWarehouseTransfersBill() { // Make a request of review 'WarehouseTransfersBill'. var oRqt = MakeSignedApiRequest(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here", new WarehouseTransfersBill { /* The 6 fields 'id', 'out_branch_id', 'out_warehouse_id', 'in_branch_id', 'in_warehouse_id', and 'review_operator_id' are required */ id = 76, out_branch_id = 1, out_warehouse_id = 1, in_branch_id = 2, in_warehouse_id = 2, review_operator_id = 1 /* Must be 1 */, }); // The serialized string is put into the request body and sent to the server. string request_body = oRqt.ToJson(); return request_body; } /// /// API: POST /api/store_deliveries/review /// /// internal static string ExampleRequestForReviewStoreDeliveriesBill() { // Make a request of review 'StoreDeliveriesBill'. var oRqt = MakeSignedApiRequest(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here", new StoreDeliveriesBill { /* The 5 fields 'id', 'branch_id', 'warehouse_id', 'in_branch_id', and 'review_operator_id' are required */ id = 77, branch_id = 1, warehouse_id = 1, in_branch_id = 2, review_operator_id = 1 /* Must be 1 */, }); // The serialized string is put into the request body and sent to the server. string request_body = oRqt.ToJson(); return request_body; } /// /// API: POST /api/store_take_deliveries/review /// /// internal static string ExampleRequestForReviewStoreTakeDeliveriesBill() { // Make a request of review 'StoreTakeDeliveriesBill'. var oRqt = MakeSignedApiRequest(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here", new StoreTakeDeliveriesBill { /* The 5 fields 'id', 'branch_id', 'warehouse_id', 'out_branch_id', and 'review_operator_id' are required */ id = 78, branch_id = 2, warehouse_id = 2, out_branch_id = 1, review_operator_id = 1 /* Must be 1 */, }); // The serialized string is put into the request body and sent to the server. string request_body = oRqt.ToJson(); return request_body; } /// /// API: POST /api/sales_invoices/review /// /// internal static string ExampleRequestForReviewSalesInvoice() { // Make a request of review 'SalesInvoice'. var oRqt = MakeSignedApiRequest(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here", new SalesInvoice { /* The 4 fields 'id', 'branch_id', 'warehouse_id', and 'review_operator_id' are required */ id = 32, branch_id = 1, warehouse_id = 1, review_operator_id = 1 /* Must be 1 */, }); // The serialized string is put into the request body and sent to the server. string request_body = oRqt.ToJson(); return request_body; } /// /// API: POST /api/store_take_deliveries/select_store_deliveries_to_create?store_deliveries_bill_id={Int64} /// Please pass in the 'id' value of the previously created 'StoreDeliveriesBill' for {Int64} in the above URL. /// /// internal static string ExampleRequestForSelectStoreDeliveriesToCreateStoreTakeDeliveriesBill() { // Make a request of create a 'StoreTakeDeliveriesBill' from a 'StoreDeliveriesBill'. var oRqt = MakeSignedApiRequest(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here", new StoreTakeDeliveriesBill { /* The 4 fields 'id', 'branch_id', 'warehouse_id', and 'out_branch_id' are required */ id = 0, branch_id = 1, warehouse_id = 1, /* The fields listed below can be filled in according to the fixed values in the example */ out_branch_id = 1 /* This value is processed by the system and you can set it to any value of 1 or greater */, details = null /* This value is processed by the system and you can set it to null or empty array [] */, }); // The serialized string is put into the request body and sent to the server. string request_body = oRqt.ToJson(); return request_body; } // **************************************** // The following entity classes used are only supported in the model class library in version 2.2.0 or later. // **************************************** /// /// API: POST /api/store_take_deliveries/create /// /// internal static string ExampleRequestForReferencedStoreDeliveriesBillToCreateStoreTakeDeliveriesBill() { // Assuming the existing 'StoreDeliveriesBill' data is as follows: // These data can be obtained through API interface: POST /api/store_deliveries/find //new StoreDeliveriesBill //{ // id = 101 /* ID of 'MO' bill */, // /* Other fields ... */ // details = new[] // { // new StoreDeliveriesDetail { id = 90001 /* ID of 'MO' detail line */, product_id = 11, quantity = 1000, /* Other fields ... */ }, // new StoreDeliveriesDetail { id = 90002 /* ID of 'MO' detail line */, product_id = 21, quantity = 2000, /* Other fields ... */ }, // new StoreDeliveriesDetail { id = 90003 /* ID of 'MO' detail line */, product_id = 31, quantity = 3000, /* Other fields ... */ }, // } //}; // Make a request of create a 'StoreTakeDeliveriesBill' referenced a existing 'StoreDeliveriesBill'. var oRqt = MakeSignedApiRequest(0L /* Fill in your Startnet user ID here */, "Fill in your 'AppKey' here", new StoreTakeDeliveriesBill { /* The 6 fields 'id', 'branch_id', 'warehouse_id', 'out_branch_id', 'referenced_store_deliveries_bill_id', and 'details' are required */ id = 0, branch_id = 2, warehouse_id = 2, out_branch_id = 1, referenced_store_deliveries_bill_id = 101 /* Must be set ID of 'MO' bill */, /* You can continue to set other fields as needed ... */ details = new[] { new StoreTakeDeliveriesDetail() { bill_id = 0, product_id = 11, quantity = 1000, referenced_store_deliveries_detail_id = 90001 /* Must be set ID of 'MO' detail line */, /* You can continue to set other fields as needed ... */ }, new StoreTakeDeliveriesDetail() { bill_id = 0, product_id = 21, quantity = 2000, referenced_store_deliveries_detail_id = 90002 /* Must be set ID of 'MO' detail line */, /* You can continue to set other fields as needed ... */ }, /* You can continue to add more items as needed ... */ }, }); // The serialized string is put into the request body and sent to the server. string request_body = oRqt.ToJson(); return request_body; } // **************************************** // The following methods are used in the model class library with version 2.4.0 or later. // **************************************** internal static double HowToMakeLocalTimezoneInRequest() { DateTimeOffset oLocalTime = DateTimeOffset.Now; TimeSpan oOffset = oLocalTime.Offset; double dLocalTimezone = oOffset.TotalHours; return dLocalTimezone; } internal static long HowToMakeTimestampInDataOfRequest() { // Step 1. Get local time with timezone. DateTimeOffset oLocalTime = DateTimeOffset.Now; // Step 2. Convert to UTC time from local time. DateTimeOffset oUtcTime = oLocalTime.ToUniversalTime(); // Step 3. Get ticks form UTC time. long lTicks = oUtcTime.Ticks; // This ticks is the timestamp. long lTimestamp = lTicks; return lTimestamp; } // **************************************** // The following entity classes used are only supported in the model class library in version 3.1.0 or later. // **************************************** /// /// APIs:
/// POST /api/customer_types/find
/// POST /api/customer_categories/find
/// POST /api/customers/create ///
internal static async void ExampleOfRequestForCreateCustomerAndRelatedInfo() { long lAppID = 0L /* Fill in your Startnet user ID here */; string sAppKey = "Fill in your 'AppKey' here"; string sApiDomain = "Fill in your API domain here"; string sCustomerTypeName = "Customer type name to search for"; string sCategoryName = "Customer category name to search for"; // Step 1. Make a request to search for one or more existing 'CustomerType'. PagedResults oRspData = await $"find".GetDataItems($"keyword={sCustomerTypeName.UrlEncode()}", 1, lAppID, sAppKey, sApiDomain); System.Collections.Generic.List oTypeExtendInfos; // Success if ((oRspData?.results?.Count ?? 0) > 0) { // Convert each 'CustomerType' to 'CustomerTypeExtendInfo' to continue creating customer data containing these customer type ID value(s). oTypeExtendInfos = oRspData.results.ConvertAll(customerType => new CustomerTypeExtendInfo { id = customerType.id, }); } // No customer type matched or an error has been raised. else { return; } // Step 2. Search for an existing 'CustomerCategory'. CustomerCategory oCategoryInfo = await sCategoryName.GetDataItem(lAppID, sAppKey, sApiDomain); int iCategoryID; // Success if (oCategoryInfo != null) { // Specify the customer category ID in the customer data to be newly created. iCategoryID = oCategoryInfo.id; } // No customer category matched or an error has been raised. else { return; } // **************************************** // The values of variables 'oTypeExtendInfos' and 'iCategoryID' in steps 1 and 2 can also be obtained from synchronized data in your local database. // **************************************** // Step 3. Make a request to create a 'Customer'. var oPostData = new Customer { /* The 3 fields 'id', 'code', and 'name' are required */ id = 0, code = "Any non empty string!", name = "Customer name", /* Associated customer type ID value(s) */ customer_types = oTypeExtendInfos, /* Associated customer category ID */ category_id = iCategoryID, price_type = 2, /* You can continue to set other fields as needed ... */ }; Customer oNewlyCustomer = await $"create".Operate(oPostData, lAppID, sAppKey, sApiDomain); // Successfully created customer if (oNewlyCustomer != null) { // Put your business code here to continue... } // Failed to create customer else { return; } } /// /// APIs:
/// POST /api/express_companies/find
/// POST /api/sales_orders/create ///
internal static async void ExampleOfRequestForCreateSalesOrder() { long lAppID = 0L /* Fill in your Startnet user ID here */; string sAppKey = "Fill in your 'AppKey' here"; string sApiDomain = "Fill in your API domain here"; string sExpressCompanyName = "Express or logistics company name to search for"; // Step 1. Search for an existing 'ExpressCompany'. ExpressCompany oExpressCompanyInfo = await sExpressCompanyName.GetDataItem(lAppID, sAppKey, sApiDomain); int iExpressCompanyID; // Success if (oExpressCompanyInfo != null) { // Specify the express or logistics company ID in the sales order data to be newly created. iExpressCompanyID = oExpressCompanyInfo.id; } // No express or logistics company matched or an error has been raised. else { return; } // **************************************** // The value of variable 'iExpressCompanyID' in step 1 can also be obtained from synchronized data in your local database. // **************************************** // Step 2. Make a request to create a 'SalesOrder'. var oPostData = new OrderSubmission { /* The 8 fields 'id', 'branch_id', 'customer_id', 'warehouse_id', 'invoice_type', 'consignee_name', 'consignee_mobile', and 'shipping_address' are required */ id = 0, branch_id = 1, customer_id = 2, warehouse_id = 3, invoice_type = 3003, consignee_name = "Consignee name", consignee_mobile = "Consignee mobile", shipping_address = "Shipping address", /* Associated express or logistics company ID */ express_company_id = iExpressCompanyID, /* You can continue to set other fields as needed ... */ carts = new ShopCart[] { /* The price of the following product is specified by you */ new ShopCart() { product_id = 1234, quantity = 10, price = 987.654321m }, /* The prices of the following 3 products are calculated by the system */ new ShopCart() { product_id = 23456, quantity = 9 } /* Implicit assignment of null to field 'price' */, new ShopCart() { product_id = 56789, quantity = 8, price = 0m }, new ShopCart() { product_id = 89012, quantity = 7, price = null }, /* You can continue to add more items as needed ... */ }, }; SalesOrder oNewlySalesOrder = await $"create".Operate(oPostData, lAppID, sAppKey, sApiDomain); // Successfully created sales order if (oNewlySalesOrder != null) { // Put your business code here to continue... } // Failed to create sales order else { return; } } #region November 2023: General or extended methods internal static ApiRequest MakeSignedApiRequest(long appID, string appKey, T requestData) { return requestData.MakeSignedApiRequest(appID, appKey); } /// /// Create an object that has been signed using the MD5 algorithm, using the current object instance as the request data. /// /// Object type for requesting data /// Request data object /// Startnet user ID used by your company /// 'AppKey' of API, provided by Startnet Software Company. /// A signed object public static ApiRequest MakeSignedApiRequest(this T requestData, long appID, string appKey) { // Make a request // Generic type 'T' can be changed to 'OrderSubmission', 'PostData', 'PostData1', 'Product' etc. according to different businesses and passed to this method. var oRqt = new ApiRequest(appID, appKey) { data = requestData, local_timezone = HowToMakeLocalTimezoneInRequest(), }; // Sign the requested data. oRqt.PSign(); return oRqt; } internal static string ToJson(this ApiRequest requestBody) { if (requestBody is null) { throw new ArgumentNullException(nameof(requestBody)); } return JsonConvert.SerializeObject(requestBody, Formatting.None); } /// /// Encode URL strings /// /// Text to be encoded /// Encoded string public static string UrlEncode(this string value) { return System.Web.HttpUtility.UrlEncode(value ?? string.Empty); } internal static async System.Threading.Tasks.Task> TryPostData(string apiURL, ApiRequest requestBody) { return await requestBody.TryPostData(apiURL); } private static Version AssemblyVersion => _AssemblyVersion ?? (_AssemblyVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version); private static Version _AssemblyVersion; private static string ApiClientVersionNumber => _ApiClientVersionNumber ?? (_ApiClientVersionNumber = $"{AssemblyVersion.Major}.{AssemblyVersion.Minor}.{AssemblyVersion.Revision}"); private static string _ApiClientVersionNumber; /// /// Attempt to submit the current object instance as the request data to the specified API URL /// /// Object type for requesting data /// The object type of the returned response data /// A signed object for submission /// The URL of the API to which the data will be submitted /// /// Return an object.
/// If the object is , it indicates that the data cannot be returned from the specified API server or that the signature verification for the returned data has failed.
/// Otherwise, it indicates that the data has been successfully returned and its signature has been verified. ///
public static async System.Threading.Tasks.Task> TryPostData(this ApiRequest requestBody, string apiURL) { if (string.IsNullOrWhiteSpace(apiURL)) { throw new ArgumentException($"'{nameof(apiURL)}' cannot be null or blank.", nameof(apiURL)); } else if (requestBody is null) { throw new ArgumentNullException(nameof(requestBody)); } try { var oApiAddress = new Uri(apiURL); if (!oApiAddress.IsAbsoluteUri) { throw new ArgumentException($"The parameter '{nameof(apiURL)}' must be an absolute URL address."); } // The serialized string is put into the request body and sent to the server. string request_body = requestBody.ToJson(); using (var oClient = new System.Net.WebClient()) { oClient.Encoding = System.Text.Encoding.UTF8; oClient.Headers.Add(System.Net.HttpRequestHeader.UserAgent, $"StartnetWebApiClient/{ApiClientVersionNumber}"); // Send request to server and wait for return. string response_body = await oClient.UploadStringTaskAsync(oApiAddress, "POST", request_body); // Parsing the response body returned by the server and verify it. return response_body.ParseAndVerifyApiResponse(requestBody.app_id, requestBody.AppKey); } } // Throwing 'UriFormatException' based on parameter 'apiURL'. catch (UriFormatException) { throw; } // Throw web exception. catch (System.Net.WebException) { throw; } // Throw other exceptions. catch (Exception) { throw; } } internal static ApiResponse ParseAndVerifyApiResponse(this string responseBody, long appID, string appKey) { if (string.IsNullOrWhiteSpace(responseBody)) { return null; } ApiResponse oRsp = null; try { // Generic type 'T' can be changed to 'SalesOrder', 'Customer', 'Product', 'Inventory[]' etc. according to different businesses and passed to this method. oRsp = JsonConvert.DeserializeObject>(responseBody); } // Capture conversion error. catch (Exception ex) { // Log 'ex' in the production environment? #if DEBUG throw ex; #endif } // Conversion succeeded. if (oRsp != null) { oRsp.PInit(appID, appKey); // Validation succeeded. if (oRsp.PVerifySignatureOnClient()) { return oRsp; } // Validation failed. else { return null; } } // Convert JSON string to object failed. else { return null; } } #endregion // **************************************** // The following entity classes used are only supported in the model class library in version 3.3.0 or later. // **************************************** /// /// APIs:
/// POST /api/branches/find
/// POST /api/sales_details_reports/find_by_dates?branch_id={Int32}&start_date=2024-05-11&end_date=2024-05-15
/// POST /api/retail_details_reports/find_by_dates?branch_id={Int32}&start_date=2024-05-12&end_date=2024-05-16
/// POST /api/purch_order_r_s_statuses/find_by_dates?branch_id={Int32}&start_date=2024-05-13&end_date=2024-05-17
/// POST /api/purch_order_c_o_s_reports/find_by_dates?start_date=2024-05-14&end_date=2024-05-18&branch_id={Int32} /// Please pass in the 'branch_id' value for {Int32} in the above URLs. ///
internal static async void ExampleOfRequestForFinding4KindsOfReports() { long lAppID = 0L /* Fill in your Startnet user ID here */; string sAppKey = "Fill in your 'AppKey' here"; string sApiDomain = "Fill in your API domain here"; string sBranchName = "Branch name to search for"; // Step 1. Search for an existing 'Branch'. Branch oBranchInfo = await sBranchName.GetDataItem(lAppID, sAppKey, sApiDomain); int iBranchID; // Success if (oBranchInfo != null) { // Search report data by the specified branch ID iBranchID = oBranchInfo.id; } // No branch matched or an error has been raised. else { return; } // **************************************** // The value of variable 'iBranchID' in step 1 can also be obtained from synchronized data in your local database. // **************************************** // Step 2. Make a request to search for any of the 4 kinds of reports listed below: 'SalesDetailsReport', 'RetailDetailsReport', 'PurchOrderReceiptShipmentStatus', and 'PurchOrderCarryOutStatusReport'. // The demonstration here only obtains data from the most recent month, and of course, the date range can be specified by you. DateTime oEndDate = DateTime.Now; DateTime oStartDate = oEndDate.AddMonths(-1); PagedResults oRspData = await $"find_by_dates".GetDataItems($"branch_id={iBranchID}&start_date={oStartDate:yyyy-MM-dd}&end_date={oEndDate:yyyy-MM-dd}", 1, lAppID, sAppKey, sApiDomain); // Success if ((oRspData?.results?.Count ?? 0) > 0) { System.Collections.Generic.List oReports = oRspData.results; // Put your business code here to continue... } // No report items found or an error has been raised. else { return; } } // **************************************** // The following entity classes used are only supported in the model class library in version 3.4.0 or later. // **************************************** /// /// APIs:
/// POST /api/warehouse_transfers/find or, /api/store_deliveries/find or, /api/store_take_deliveries/find
/// POST /api/warehouse_transfers/modify or, /api/store_deliveries/modify or, /api/store_take_deliveries/modify ///
internal static async void ExampleOfRequestForModifyWarehouseTransfersOrStoreDeliveriesOrStoreTakeDeliveriesBill() { long lAppID = 0L /* Fill in your Startnet user ID here */; string sAppKey = "Fill in your 'AppKey' here"; string sApiDomain = "Fill in your API domain here"; string sBillCode = "Bill code to search for"; string sBillDate = "2024-05-17" /* Modify appropriately based on the actual bill date */; // **************************************** // Type 'WarehouseTransfersBill' in the following code can be replaced with 'StoreDeliveriesBill' or 'StoreTakeDeliveriesBill' according to different businesses. // Type 'WarehouseTransfersDetail' in the following code can be replaced with 'StoreDeliveriesDetail' or 'StoreTakeDeliveriesDetail' according to different businesses. // **************************************** // Step 1. Search for an existing 'WarehouseTransfersBill' or, 'StoreDeliveriesBill' or, 'StoreTakeDeliveriesBill'. string sDateRangeQuery = $"start_date={sBillDate.UrlEncode()}&end_date={sBillDate.UrlEncode()}"; WarehouseTransfersBill oBillInfo = await sBillCode.GetDataItem("Code", sDateRangeQuery, lAppID, sAppKey, sApiDomain); // Success if (oBillInfo != null) { // Step 2. Please modify the bill or its details here before transferring it to the server... // Kind 1: The 7 fields in the warehouse transfers bill data listed below can be modified by you. oBillInfo.in_branch_id = 1; oBillInfo.in_warehouse_id = 2; oBillInfo.out_branch_id = 3; oBillInfo.out_warehouse_id = 4; oBillInfo.custom_bill_code = "New custom bill code"; oBillInfo.remark = "New bill remark"; oBillInfo.handler_id = 5; // Kind 2: The 6 fields in the store deliveries bill data listed below can be modified by you. //var oDemo2 = new StoreDeliveriesBill //{ // branch_id = 1, // warehouse_id = 2, // in_branch_id = 3, // custom_bill_code = "New custom bill code", // remark = "New bill remark", // handler_id = 4, //}; // Kind 3: The 6 fields in the store take deliveries bill data listed below can be modified by you. //var oDemo3 = new StoreTakeDeliveriesBill //{ // branch_id = 1, // warehouse_id = 2, // out_branch_id = 3, // custom_bill_code = "New custom bill code", // remark = "New bill remark", // handler_id = 4, //}; System.Collections.Generic.ICollection oBillDetails = oBillInfo.details; // The following demonstration shows how to modify existing detail line data if necessary. foreach (WarehouseTransfersDetail detail in oBillDetails) { if (detail.product_id == 66) { // The 3 fields in the detail line data listed below can be modified by you. detail.product_id = 66; detail.quantity = 77; detail.memo = "New detail line memo"; } } // The following demonstration shows how to add a new detail line data if necessary. oBillDetails.Add(new WarehouseTransfersDetail { /* Only set to 0 when adding a new detail line data, and do not need to change this value when modifying existing detail line data */ id = 0L, /* Only set to 0 when adding a new detail line data, and do not need to change this value when modifying existing detail line data */ bill_id = 0L, product_id = 8, quantity = 9, memo = "Memo", }); // The following demonstration shows how to delete one or more existing detail line data if necessary. var oRemovedDetails = new System.Collections.Generic.List(); foreach (WarehouseTransfersDetail detail in oBillDetails) { if (detail.product_id == 8765 || detail.product_id == 43210) { oRemovedDetails.Add(detail); } } for (int i = oRemovedDetails.Count - 1; i >= 0; i--) { oBillDetails.Remove(oRemovedDetails[i]); } } // No bill matched or an error has been raised. else { return; } // Step 3. Make a request to update a 'WarehouseTransfersBill'(MZ) or, 'StoreDeliveriesBill'(MO) or, 'StoreTakeDeliveriesBill'(MI). // Call API to update data. // Kind 1: /api/warehouse_transfers/modify // Kind 2: /api/store_deliveries/modify // Kind 3: /api/store_take_deliveries/modify WarehouseTransfersBill oNewlyData = await $"modify".Operate(oBillInfo, lAppID, sAppKey, sApiDomain); // Success if (oNewlyData != null) { // Put your business code here to continue... } // Update failed or an error has been raised. else { return; } } /// /// The APIs listed below can only be requested once per terminal within 60 seconds.
/// POST /api/branches
/// POST /api/warehouses
/// POST /api/employees/{branch_id}
/// POST /api/membership_classes
/// POST /api/vips/v2
/// POST /api/customers
/// POST /api/customer_types
/// POST /api/customer_categories
/// POST /api/customer_delivery_addresses or, /api/customer_delivery_addresses/{customer_id}/list
/// POST /api/product_categories
/// POST /api/product_brands
/// POST /api/products
/// POST /api/express_companies
/// POST /api/sales_orders
/// POST /api/sales_order_statistics
/// POST /api/retail_orders
/// POST /api/replenishment_orders
/// POST /api/inventories/{warehouse_id}
/// POST /api/tax_invoice_statistics
///
/// The APIs listed below can only be requested once per terminal within 30 seconds.
/// POST /api/branches/find or, find_by_ids
/// POST /api/warehouses/find or, find_by_ids
/// POST /api/employees/{branch_id}/find or, find_by_ids
/// POST /api/membership_classes/find or, find_by_ids
/// POST /api/vips/v2/find
/// POST /api/customers/find or, find_by_ids
/// POST /api/customer_types/find or, find_by_ids
/// POST /api/customer_categories/find or, find_by_ids
/// POST /api/customer_delivery_addresses/find or, find_by_ids or, {customer_id}/find
/// POST /api/product_categories/find or, find_by_ids
/// POST /api/product_brands/find or, find_by_ids
/// POST /api/products/find or, find_by_ids
/// POST /api/express_companies/find or, find_by_ids
/// POST /api/purch_order_c_o_s_reports/find_by_dates or, find_by_dates_keyword
/// POST /api/purch_order_r_s_statuses/find_by_dates or, find_by_dates_keyword
/// POST /api/sales_orders/find_by_dates or, find_by_dates_keyword or, find_by_ids
/// POST /api/sales_order_statistics/find or, find_by_dates or, find_by_dates_keyword or, find_by_id
/// POST /api/sales_details_reports/find_by_dates or, find_by_dates_keyword
/// POST /api/sales_invoices/find_by_dates or, find_by_dates_keyword
/// POST /api/retail_orders/find_by_dates or, find_by_dates_keyword or, find_by_ids
/// POST /api/retail_details_reports/find_by_dates or, find_by_dates_keyword
/// POST /api/retail_details_reports/v2/find_by_dates or, find_by_dates_keyword
/// POST /api/membership_points/find_by_dates or, find_by_dates_keyword
/// POST /api/replenishment_orders/find_by_dates or, find_by_dates_keyword or, find_by_ids
/// POST /api/warehouse_transfers/find
/// POST /api/store_deliveries/find
/// POST /api/store_deliveries/v2/find
/// POST /api/store_take_deliveries/find
/// POST /api/inventories/{warehouse_id}/find_by_product_ids
/// POST /api/inventories/v2/find or, find_by_ids
/// POST /api/inventories/v3 or, /api/inventories/v3/find
/// POST /api/tax_invoice_statistics/find or, find_by_dates or, find_by_dates_keyword
///
/// The APIs listed below can only be requested once per terminal within 15 seconds.
/// POST /api/branches/{id}
/// POST /api/warehouses/{id}
/// POST /api/employees/{branch_id}/{id}
/// POST /api/membership_classes/{id}
/// POST /api/vips/v2/{id}
/// POST /api/customers/{id}
/// POST /api/customer_types/{id}
/// POST /api/customer_categories/{id}
/// POST /api/customer_delivery_addresses/{id}
/// POST /api/product_categories/{id}
/// POST /api/product_brands/{id}
/// POST /api/products/{id}
/// POST /api/express_companies/{id}
/// POST /api/sales_orders/{id}
/// POST /api/sales_order_statistics/{id}
/// POST /api/retail_orders/{id}
/// POST /api/replenishment_orders/{id}
/// POST /api/inventories/{warehouse_id}/{product_id}
/// POST /api/inventories/v2/{branch_id}/{product_id} ///
internal static void ExampleOfRequestForGetAllPagedData() { var oTask = new System.Threading.Tasks.Task(async () => { long lAppID = 0L /* Fill in your Startnet user ID here */; string sAppKey = "Fill in your 'AppKey' here"; string sApiDomain = "Fill in your API domain here"; string sBillDate1 = "2024-05-22" /* Modify appropriately based on the actual bill starting date */; string sBillDate2 = "2024-05-23" /* Modify appropriately based on the actual bill ending date */; int iPageNumber = 1; var oFullData = new System.Collections.Generic.List(); NextPage: // Make a request to obtaining 'RetailDetailsReport' data limited to a specific page. PagedResults oRspData = await $"find_by_dates".GetDataItems($"start_date={sBillDate1.UrlEncode()}&end_date={sBillDate2.UrlEncode()}", iPageNumber, lAppID, sAppKey, sApiDomain); // Success if ((oRspData?.total_count ?? 0) > 0 && oRspData.total_pages > 0) { if ((oRspData.results?.Count ?? 0) > 0) { // Of course, you can save the obtained paged data directly to your local database without using this variable to temporarily store the data. oFullData.AddRange(oRspData.results); } if (iPageNumber < oRspData.total_pages) { // Please sleep for 60, 30 or 15 seconds before obtaining the next paged data. System.Threading.Thread.Sleep(30 /* Please set the time to 60, 30 or 15 seconds based on calling different APIs */ * 1000); iPageNumber++; goto NextPage; } // The data in variable 'oFullData' has been fully loaded. // Put your business code here to continue... } // No data matched or an error has been raised. else { if (oFullData.Count > 0) { // Although the data in variable 'oFullData' is incomplete, it can still be stored in your local database. // Put your business code here to continue... } return; } }); oTask.Start(); } // **************************************** // 以下代码中所涉及的实体类仅可在 4.0.0 或更高版本的 API 模型类库中使用。 // The following entity classes used are only supported in the model class library in version 4.0.0 or later. // **************************************** /// /// APIs:
/// POST /api/branches/find
/// POST /api/membership_classes/find
/// POST /api/vips/v2/create ///
internal static async void ExampleOfRequestForCreateMembershipCardInfo() { long lAppID = 0L /* 这里需更改为贵公司使用的启网用户号 Fill in your Startnet user ID here */; string sAppKey = "这里需更改为贵公司使用的“AppKey” Fill in your 'AppKey' here"; string sApiDomain = "这里需更改为贵公司使用的 API 域名 Fill in your API domain here"; string sBranchCode = "欲查找的机构(门店)编码 Branch or store code to search for"; string sCardClassCode = "欲查找的会员卡分类编码 Membership card class code to search for"; // 【第一步】先查找出一个已存在于数据库的机构(门店)数据项目“Branch”。 // Step 1. Search for an existing 'Branch'. Branch oBranchInfo = await sBranchCode.GetDataItem("Code", lAppID, sAppKey, sApiDomain); int iBranchID; // 已成功找到数据项目 // Success if (oBranchInfo != null) { // 该机构(门店)数据项目的 ID 将与欲新建的会员卡数据项目关联。 // Specify the branch ID in the membership card data to be newly created. iBranchID = oBranchInfo.id; } // 找不到机构(门店)数据项目,或所调用的 API 已发生错误。 // No branch matched or an error has been raised. else { return; } // 【第二步】再查找出一个已存在于数据库的会员卡分类数据项目“MembershipClass”。 // Step 2. Search for an existing 'MembershipClass'. MembershipClass oCardClassInfo = await sCardClassCode.GetDataItem(lAppID, sAppKey, sApiDomain); int iCardClassID; // 已成功找到数据项目 // Success if (oCardClassInfo != null) { // 该会员卡分类数据项目的 ID 将与欲新建的会员卡数据项目关联。 // Specify the membership card class ID in the membership card data to be newly created. iCardClassID = oCardClassInfo.id; } // 找不到会员卡分类数据项目,或所调用的 API 已发生错误。 // No membership card class matched or an error has been raised. else { return; } // **************************************** // 就上面【第一步】和【第二步】提到的“iBranchID”和“iCardClassID”变量值,也可从您本地服务器中已自 API 处同步的数据库副本中获取 // The values of variables 'iBranchID' and 'iCardClassID' in steps 1 and 2 can also be obtained from synchronized data in your local database. // **************************************** // 【第三步】生成 API 请求以创建一个会员卡数据项目“Membership2”。 // Step 3. Make a request to create a 'Membership2'. var oPostData = new MembershipSubmission { /* 以下列出的五个属性值为必填字段:“id”、“name”、“class_id”、“branch_id”和“mobile” */ /* The 5 fields 'id', 'name', 'class_id', 'branch_id', and 'mobile' are required */ id = 0 /* 该值必须设置为零 Must be 0 */, name = "会员姓名,为任何非空字符串 Member name, any non empty string!", branch_id = iBranchID /* 关联该机构(门店)的 ID 值 Associated branch ID */, class_id = iCardClassID /* 关联该会员卡分类的 ID 值 Associated membership card class ID */, mobile = "手机号码 Mobile number", /* 可按需要继续设置其它非必需的字段…… */ /* You can continue to set other fields as needed ... */ code = "可由您指定的会员卡号,如指定为 null 或空字符串(缺省值),则按系统规则自动生成该卡号。 Specify the card code, which is generated by the system when specified as null or when an empty string value is passed in.", disabled = true /* 设置该指示值可停用当前会员卡 Use this flag to deactivate the membership card */, tel = "固话号码 Telephone number", email = "电子邮箱地址 E-Mail address", address = "居住地址 Residential address", contact_address = "联系地址 Contact address", postal_code = "邮政编码 Postal code", gender = false /* 会员的性别:false=男性,true=女性 Member gender: Male(value is false), Female(value is true) */, birthday = new DateTime(2024, 6, 3) /* 会员生日日期 Member birthday date */, }; Membership2 oNewlyData = await $"create".Operate(oPostData, lAppID, sAppKey, sApiDomain); // 已成功创建会员卡数据项目 // Successfully created membership card info if (oNewlyData != null) { // 可继续在此处编写贵方的后续业务代码…… // Put your business code here to continue... } // 无法创建会员卡数据项目,或所调用的 API 已发生错误。 // Failed to create membership card info else { return; } } #region June 2024: General or extended methods internal static async System.Threading.Tasks.Task GetDataItem(this int itemID, long appID, string appKey, string apiDomain) where T : class { return await itemID.GetDataItem(null, appID, appKey, apiDomain); } internal static async System.Threading.Tasks.Task GetDataItem(this int itemID, string queryString, long appID, string appKey, string apiDomain) where T : class { return await itemID.GetDataItem(null, queryString, appID, appKey, apiDomain); } internal static async System.Threading.Tasks.Task GetDataItem(this int itemID, string operationName, string queryString, long appID, string appKey, string apiDomain) where T : class { return await GetDataItem(appID, appKey, apiDomain, operationName, itemID, null, null, queryString); } internal static async System.Threading.Tasks.Task GetDataItem(this long itemID, long appID, string appKey, string apiDomain) where T : class { return await itemID.GetDataItem(null, appID, appKey, apiDomain); } internal static async System.Threading.Tasks.Task GetDataItem(this long itemID, string queryString, long appID, string appKey, string apiDomain) where T : class { return await itemID.GetDataItem(null, queryString, appID, appKey, apiDomain); } internal static async System.Threading.Tasks.Task GetDataItem(this long itemID, string operationName, string queryString, long appID, string appKey, string apiDomain) where T : class { return await GetDataItem(appID, appKey, apiDomain, operationName, itemID, null, null, queryString); } internal static async System.Threading.Tasks.Task GetDataItem(this string codeOrKeyword, long appID, string appKey, string apiDomain) where T : class { return await codeOrKeyword.GetDataItem(null, appID, appKey, apiDomain); } internal static async System.Threading.Tasks.Task GetDataItem(this string codeOrKeyword, string keywordMatchMode, long appID, string appKey, string apiDomain) where T : class { return await codeOrKeyword.GetDataItem(keywordMatchMode, null, appID, appKey, apiDomain); } internal static async System.Threading.Tasks.Task GetDataItem(this string codeOrKeyword, string keywordMatchMode, string appendQueryString, long appID, string appKey, string apiDomain) where T : class { return await codeOrKeyword.GetDataItem(null, keywordMatchMode, appendQueryString, appID, appKey, apiDomain); } internal static async System.Threading.Tasks.Task GetDataItem(this string codeOrKeyword, string operationName, string keywordMatchMode, string appendQueryString, long appID, string appKey, string apiDomain) where T : class { return await GetDataItem(appID, appKey, apiDomain, operationName, null, codeOrKeyword, keywordMatchMode, appendQueryString); } private static async System.Threading.Tasks.Task GetDataItem(long appID, string appKey, string apiDomain, string operationName, long? itemID, string codeOrKeyword, string keywordMatchMode, string appendQueryString) where T : class { if (string.IsNullOrWhiteSpace(appKey)) { throw new ArgumentException($"'{nameof(appKey)}' cannot be null or blank.", nameof(appKey)); } else if (string.IsNullOrWhiteSpace(apiDomain)) { throw new ArgumentException($"'{nameof(apiDomain)}' cannot be null or blank.", nameof(apiDomain)); } bool bSearchByDataID = itemID != null; if (!bSearchByDataID && string.IsNullOrWhiteSpace(codeOrKeyword)) { throw new ArgumentException($"'{nameof(codeOrKeyword)}' cannot be null or blank.", nameof(codeOrKeyword)); } if (string.IsNullOrWhiteSpace(apiDomain.GetBaseURL())) { return null; } // 以数据项目的 ID 来进行查找 // Search for a data item by ID if (bSearchByDataID) { var oPostData = new PostData { include_details = true, timestamp = HowToMakeTimestampInDataOfRequest(), }; operationName = operationName.Replace('\\', '/').TrimStart('/'); operationName = string.IsNullOrWhiteSpace(operationName) ? $"{itemID}" : operationName; return await operationName.Operate(oPostData, appendQueryString, appID, appKey, apiDomain); } // 以数据项目的编码或其它关键词来进行查找 // Search for a data item by code or other keywords else { var oPostData = new PostData1 { page_number = 1, page_size = 1, include_details = true, timestamp = HowToMakeTimestampInDataOfRequest(), }; operationName = operationName.Replace('\\', '/').TrimStart('/'); operationName = string.IsNullOrWhiteSpace(operationName) ? $"find" : operationName; appendQueryString = string.IsNullOrWhiteSpace(appendQueryString) ? string.Empty : $"&{appendQueryString}"; string sByModeParam = string.IsNullOrWhiteSpace(keywordMatchMode) ? string.Empty : $"by_mode={keywordMatchMode}&"; string sQueryString = $"{sByModeParam}keyword={codeOrKeyword.UrlEncode()}{appendQueryString}"; PagedResults oRspData = await operationName.Operate>(oPostData, sQueryString, appID, appKey, apiDomain); if ((oRspData?.results?.Count ?? 0) == 1) { return oRspData.results[0]; } else { return null; } } } private static readonly System.Collections.Generic.Dictionary BaseURLs = new System.Collections.Generic.Dictionary { { typeof(Branch).FullName, "/api/branches" }, { typeof(CustomerCategory).FullName, "/api/customer_categories" }, { typeof(CustomerDeliveryAddress).FullName, "/api/customer_delivery_addresses" }, { typeof(Customer).FullName, "/api/customers" }, { typeof(CustomerType).FullName, "/api/customer_types" }, { typeof(Employee).FullName, "/api/employees" }, { typeof(ExpressCompany).FullName, "/api/express_companies" }, { typeof(Inventory).FullName, "/api/inventories" }, { typeof(Inventory2).FullName, "/api/inventories/v2" }, { typeof(Inventory3).FullName, "/api/inventories/v3" }, { typeof(MembershipClass).FullName, "/api/membership_classes" }, { typeof(MembershipPoint).FullName, "/api/membership_points" }, { typeof(MembershipPointsIncreaseBill).FullName, "/api/membership_points" }, { typeof(MembershipPointsRealizeBill).FullName, "/api/membership_points" }, { typeof(Membership2).FullName, "/api/vips/v2" }, { typeof(MembershipSubmission).FullName, "/api/vips/v2" }, { typeof(Operator).FullName, "/api/operators" }, { typeof(ProductBrand).FullName, "/api/product_brands" }, { typeof(ProductCategory).FullName, "/api/product_categories" }, { typeof(Product).FullName, "/api/products" }, { typeof(PurchaseOrderCarryOutStatus).FullName, "/api/purchase_order_carry_out_statuses" }, { typeof(PurchOrderCarryOutStatusReport).FullName, "/api/purch_order_c_o_s_reports" }, { typeof(PurchOrderReceiptShipmentStatus).FullName, "/api/purch_order_r_s_statuses" }, { typeof(ReplenishmentOrder).FullName, "/api/replenishment_orders" }, { typeof(RetailDetailsReport).FullName, "/api/retail_details_reports" }, { typeof(RetailDetailsReport2).FullName, "/api/retail_details_reports/v2" }, { typeof(RetailOrder).FullName, "/api/retail_orders" }, { typeof(SalesDetailsReport).FullName, "/api/sales_details_reports" }, { typeof(SalesInvoice).FullName, "/api/sales_invoices" }, { typeof(SalesOrder).FullName, "/api/sales_orders" }, { typeof(OrderSubmission).FullName, "/api/sales_orders" }, { typeof(SalesOrderStatistic).FullName, "/api/sales_order_statistics" }, { typeof(StoreDeliveriesBill).FullName, "/api/store_deliveries" }, { typeof(StoreDeliveriesBill2).FullName, "/api/store_deliveries/v2" }, { typeof(StoreTakeDeliveriesBill).FullName, "/api/store_take_deliveries" }, { typeof(TaxInvoiceStatistic).FullName, "/api/tax_invoice_statistics" }, { typeof(Vender).FullName, "/api/venders" }, { typeof(Warehouse).FullName, "/api/warehouses" }, { typeof(WarehouseTransfersBill).FullName, "/api/warehouse_transfers" }, }; private static string GetBaseURL(this string apiDomain) where T : class { if (string.IsNullOrWhiteSpace(apiDomain)) { throw new ArgumentException($"'{nameof(apiDomain)}' cannot be null or blank.", nameof(apiDomain)); } Type oDataType = typeof(T); string sKey = oDataType.FullName; if (BaseURLs.ContainsKey(sKey)) { string sRootPath = BaseURLs[sKey]; return $"https://{apiDomain}{sRootPath}"; } else { throw new NotImplementedException($"The API root path corresponding to type '{oDataType.Name}' has not been defined yet."); } } internal static async System.Threading.Tasks.Task> GetDataItems(this string operationName, int pageNumber, long appID, string appKey, string apiDomain) where T : class { return await operationName.GetDataItems(null, pageNumber, appID, appKey, apiDomain); } internal static async System.Threading.Tasks.Task> GetDataItems(this string operationName, string queryString, int pageNumber, long appID, string appKey, string apiDomain) where T : class { return await operationName.GetDataItems>(queryString, pageNumber, appID, appKey, apiDomain); } internal static async System.Threading.Tasks.Task GetDataItems(this string operationName, string queryString, int pageNumber, long appID, string appKey, string apiDomain) where TEntity : class where TData : PostData1, new() where TResult : PagedResults { return await operationName.GetDataItems(queryString, null, pageNumber, appID, appKey, apiDomain); } internal static async System.Threading.Tasks.Task GetDataItems(this string operationName, string queryString, System.Collections.Generic.List externalIDsForSearch, int pageNumber, long appID, string appKey, string apiDomain) where TEntity : class where TData : PostData1, new() where TResult : PagedResults { if (string.IsNullOrWhiteSpace(appKey)) { throw new ArgumentException($"'{nameof(appKey)}' cannot be null or blank.", nameof(appKey)); } else if (string.IsNullOrWhiteSpace(apiDomain)) { throw new ArgumentException($"'{nameof(apiDomain)}' cannot be null or blank.", nameof(apiDomain)); } else if (operationName is null) { throw new ArgumentNullException(nameof(operationName)); } if (string.IsNullOrWhiteSpace(apiDomain.GetBaseURL())) { return null; } var oPostData = new TData { page_number = pageNumber, page_size = 5000, include_details = true, timestamp = HowToMakeTimestampInDataOfRequest(), }; if (oPostData is PostData2 oPostData2) { oPostData2.external_ids = externalIDsForSearch; } else if (oPostData is PostData2 oPostData3) { oPostData3.external_ids = externalIDsForSearch; } return await operationName.Operate(oPostData, queryString, appID, appKey, apiDomain); } internal static async System.Threading.Tasks.Task Operate(this string operationName, TEntity data, long appID, string appKey, string apiDomain) where TEntity : class where TResult : class { return await operationName.Operate(data, string.Empty, appID, appKey, apiDomain); } internal static async System.Threading.Tasks.Task Operate(this string operationName, TEntity data, string queryString, long appID, string appKey, string apiDomain) where TEntity : class where TResult : class { return await operationName.Operate(data, queryString, appID, appKey, apiDomain); } internal static async System.Threading.Tasks.Task Operate(this string operationName, TData data, string queryString, long appID, string appKey, string apiDomain) where TEntity : class where TData : class where TResult : class { if (string.IsNullOrWhiteSpace(appKey)) { throw new ArgumentException($"'{nameof(appKey)}' cannot be null or blank.", nameof(appKey)); } else if (string.IsNullOrWhiteSpace(apiDomain)) { throw new ArgumentException($"'{nameof(apiDomain)}' cannot be null or blank.", nameof(apiDomain)); } else if (operationName is null) { throw new ArgumentNullException(nameof(operationName)); } else if (data is null) { throw new ArgumentNullException(nameof(data)); } string sBaseURL = apiDomain.GetBaseURL(); if (string.IsNullOrWhiteSpace(sBaseURL)) { return null; } ApiRequest oRqt = data.MakeSignedApiRequest(appID, appKey); operationName = operationName.Replace('\\', '/').Trim('/'); operationName = string.IsNullOrWhiteSpace(operationName) ? string.Empty : $"/{operationName}"; queryString = string.IsNullOrWhiteSpace(queryString) ? string.Empty : $"?{queryString.TrimStart('?')}"; string sApiURL = $"{sBaseURL}{operationName}{queryString}"; ApiResponse oRsp = await oRqt.TryPostData(sApiURL); if (oRsp?.data != null && oRsp.result_code == ErrorCode.None) { return oRsp.data; } else { return null; } } #endregion /// /// APIs:
/// POST /api/vips/v2/find or, /api/vips/v2/{id}
/// POST /api/vips/v2/modify ///
internal static async void ExampleOfRequestForModifyMembershipCardInfo() { long lAppID = 0L /* 这里需更改为贵公司使用的启网用户号 Fill in your Startnet user ID here */; string sAppKey = "这里需更改为贵公司使用的“AppKey” Fill in your 'AppKey' here"; string sApiDomain = "这里需更改为贵公司使用的 API 域名 Fill in your API domain here"; // 【第一步】先查找出一个已存在于数据库的会员卡数据项目“Membership2”。 // Step 1. Search for an existing 'Membership2'. string sCardCode = "欲查找的会员卡编码(卡号) Membership card code to search for"; Membership2 oCardInfo = await sCardCode.GetDataItem("Code", lAppID, sAppKey, sApiDomain); // **************************************** // 以下为按会员卡 ID 值来进行查找数据的示例 // Example for search by membership card info ID // **************************************** //int iCardID = 1 /* 欲查找的会员卡数据项目的 ID 值 Membership card info ID to search for */; //Membership2 oCardInfo = await iCardID.GetDataItem(lAppID, sAppKey, sApiDomain); // 将对象“oCardInfo”从类型“Membership2”转换为其基类型“MembershipSubmission”的对象“oSavedInfo” // Convert object 'oCardInfo' from type 'Membership2' to type 'MembershipSubmission' object 'oSavedInfo' MembershipSubmission oSavedInfo = oCardInfo; // 已成功找到数据项目并转换 // Success if (oSavedInfo != null) { // 【第二步】您可以在此处更改会员卡信息(如果是不需更改的字段值则必须保留其原来值以提交到服务器),更改后请将有关改动回传到 API 服务器…… // Step 2. Please modify the membership card info here before transferring it to the server... // 您可更改以下列出的会员卡数据项目中的十个属性的字段值 // The 10 fields in the membership card data listed below can be modified by you. oSavedInfo.name = "新的会员姓名 New member name"; oSavedInfo.disabled = true /* 设置该指示值可停用当前会员卡 Use this flag to deactivate the membership card */; oSavedInfo.mobile = "新的手机号码 New mobile number"; oSavedInfo.tel = "新的固话号码 New telephone number"; oSavedInfo.email = "新的电子邮箱地址 New address of E-Mail"; oSavedInfo.address = "新的居住地址 New residential address"; oSavedInfo.contact_address = "新的联系地址 New contact address"; oSavedInfo.postal_code = "新的邮政编码 New postal code"; oSavedInfo.gender = true /* 会员的性别:false=男性,true=女性 Member gender: Male(value is false), Female(value is true) */; oSavedInfo.birthday = new DateTime(2024, 6, 3) /* 会员生日日期 Member birthday date */; } // 找不到会员卡数据项目,或所调用的 API 已发生错误。 // No membership card info matched or an error has been raised. else { return; } // 【第三步】生成 API 请求以更新一个会员卡数据项目“Membership2”。 // 调用相关的 API 以更新数据 // Step 3. Make a request to update a 'Membership2'. // Call API to update data. Membership2 oNewlyData = await $"modify".Operate(oSavedInfo, lAppID, sAppKey, sApiDomain); // 已成功更新会员卡数据项目 // Success if (oNewlyData != null) { // 可继续在此处编写贵方的后续业务代码…… // Put your business code here to continue... } // 无法更新会员卡数据项目,或所调用的 API 已发生错误。 // Update failed or an error has been raised. else { return; } } /// /// API: POST /api/vips/v2/find?keyword={SearchKeywords}&current_points_balance_required=true /// internal static async void ExampleOfRequestForFindingMembershipCurrentPointsAndBalance() { long lAppID = 0L /* 这里需更改为贵公司使用的启网用户号 Fill in your Startnet user ID here */; string sAppKey = "这里需更改为贵公司使用的“AppKey” Fill in your 'AppKey' here"; string sApiDomain = "这里需更改为贵公司使用的 API 域名 Fill in your API domain here"; string sMemberName = "欲查找的会员姓名 Member name to search for"; // 生成 API 请求以查找一个或多个已存在于数据库中的会员卡数据项目,并且带有会员的当前积分和余额等数据。 // 当该 URL 的查询字符串中的参数“current_points_balance_required”指定为 true 时,则返回的数据中会带有会员的当前积分和余额,否则不返回这两种数据的值。 // Make a request to search for one or more existing 'Membership2' with member's current points and balance. // When 'current_points_balance_required' in the query string of the URL is specified as true, the returned data will include the member's current points and balance. Otherwise, the values of these two types of data will not be returned. PagedResults oRspData = await $"find".GetDataItems($"keyword={sMemberName.UrlEncode()}¤t_points_balance_required=true", 1, lAppID, sAppKey, sApiDomain); // 已成功找到数据项目 // Success if ((oRspData?.results?.Count ?? 0) > 0) { System.Collections.Generic.List oCardInfos = oRspData.results; // 可继续在此处编写贵方的后续业务代码…… // Put your business code here to continue... } // 找不到任何匹配的数据项目,或所调用的 API 已发生错误。 // No data items found or an error has been raised. else { return; } } /// /// APIs:
/// POST /api/branches/find
/// POST /api/vips/v2/find
/// POST /api/membership_points/increase or, /api/membership_points/realize ///
internal static async void ExampleOfRequestForIncreaseOrRealizeMembershipPoints() { long lAppID = 0L /* 这里需更改为贵公司使用的启网用户号 Fill in your Startnet user ID here */; string sAppKey = "这里需更改为贵公司使用的“AppKey” Fill in your 'AppKey' here"; string sApiDomain = "这里需更改为贵公司使用的 API 域名 Fill in your API domain here"; string sBranchCode = "欲查找的积分发生门店(机构)编码 Store or branch code to search for"; string sCardCode = "欲查找的会员卡编码(卡号) Membership card code to search for"; // 【第一步】先查找出一个已存在于数据库的门店(机构)数据项目“Branch”。 // Step 1. Search for an existing 'Branch'. Branch oBranchInfo = await sBranchCode.GetDataItem("Code", lAppID, sAppKey, sApiDomain); int iBranchID; // 已成功找到数据项目 // Success if (oBranchInfo != null) { // 该门店(机构)数据项目的 ID 将与新建的会员卡积分增加或减少单据的数据项目关联。 // The ID of the branch data item will be associated with the data item of the newly created membership card points increase or realize bill. iBranchID = oBranchInfo.id; } // 找不到门店(机构)数据项目,或所调用的 API 已发生错误。 // No branch matched or an error has been raised. else { return; } // 【第二步】再查找出一个已存在于数据库的会员卡数据项目“Membership2”。 // Step 2. Search for an existing 'Membership2'. Membership2 oCardInfo = await sCardCode.GetDataItem("Code", lAppID, sAppKey, sApiDomain); int iCardID; // 已成功找到数据项目 // Success if (oCardInfo != null) { // 该会员卡数据项目的 ID 将与新建的会员卡积分增加或减少单据的数据项目关联。 // The ID of the membership card data item will be associated with the data item of the newly created membership card points increase or realize bill. iCardID = oCardInfo.id; } // 找不到会员卡数据项目,或所调用的 API 已发生错误。 // No membership card info matched or an error has been raised. else { return; } // **************************************** // 就上面【第一步】和【第二步】提到的“iBranchID”和“iCardID”变量值,也可从您本地服务器中已自 API 处同步的数据库副本中获取 // The values of variables 'iBranchID' and 'iCardID' in steps 1 and 2 can also be obtained from synchronized data in your local database. // **************************************** // 【第三步】生成 API 请求以创建会员卡积分增加或减少单据的数据项目“MembershipPointsIncreaseBill”或“MembershipPointsRealizeBill”。 // Step 3. Make a request to create a 'MembershipPointsIncreaseBill' or, 'MembershipPointsRealizeBill'. // 方法一:生成 API 请求以创建会员卡积分增加单据的数据项目“MembershipPointsIncreaseBill”。 // Kind 1: Make a request to create a 'MembershipPointsIncreaseBill'. var oPostData = new MembershipPointsIncreaseBill { /* 以下列出的五个属性值为必填字段:“id”、“branch_id”、“membership_card_id”、“consume_amt”和“custom_bill_code” */ /* The 5 fields 'id', 'branch_id', 'membership_card_id', 'consume_amt', and 'custom_bill_code' are required */ id = 0 /* 该值必须设置为零 Must be 0 */, branch_id = iBranchID /* 关联该发生门店(机构)的 ID 值 Associated branch ID */, membership_card_id = iCardID /* 关联该会员卡的 ID 值 Associated membership card ID */, consume_amt = 123.450000m /* 消费金额(系统会根据该金额自动计算积分,因此无需传入积分数量) Consume amount */, custom_bill_code = "自定义单据号(即贵方系统中的唯一单据编号) Custom bill code", /* 可按需要继续设置其它非必需的字段…… */ /* You can continue to set other fields as needed ... */ memo = "备注内容(可根据需要传入积分发生原因) Memo", }; // 方法二:生成 API 请求以创建会员卡积分减少单据的数据项目“MembershipPointsRealizeBill”。 // Kind 2: Make a request to create a 'MembershipPointsRealizeBill'. //var oPostData_Exp2 = new MembershipPointsRealizeBill //{ // /* 以下列出的五个属性值为必填字段:“id”、“branch_id”、“membership_card_id”、“spending_points”和“custom_bill_code” */ // /* The 5 fields 'id', 'branch_id', 'membership_card_id', 'spending_points', and 'custom_bill_code' are required */ // id = 0 /* 该值必须设置为零 Must be 0 */, // branch_id = iBranchID /* 关联该发生门店(机构)的 ID 值 Associated branch ID */, // membership_card_id = iCardID /* 关联该会员卡的 ID 值 Associated membership card ID */, // spending_points = 654321.000000m /* 兑奖所消耗的积分数量 Spending points */, // custom_bill_code = "自定义单据号(即贵方系统中的唯一单据编号) Custom bill code", // /* 可按需要继续设置其它非必需的字段…… */ // /* You can continue to set other fields as needed ... */ // memo = "备注内容(可根据需要传入积分发生原因) Memo", //}; // 调用相关 API 方法以创建会员卡积分增加或减少单据 // 方法一:/api/membership_points/increase // 方法二:/api/membership_points/realize // Call API to create bill. // Kind 1: /api/membership_points/increase // Kind 2: /api/membership_points/realize MembershipPointsIncreaseBill oNewlyData = await $"increase".Operate(oPostData, lAppID, sAppKey, sApiDomain); // 已成功创建会员卡积分增加或减少单据的数据项目 // Successfully created membership card points increase or realize bill if (oNewlyData != null) { // 可继续在此处编写贵方的后续业务代码…… // Put your business code here to continue... } // 无法创建会员卡积分增加或减少单据的数据项目,或所调用的 API 已发生错误。 // Failed to create membership card points increase or realize bill else { return; } } /// /// API: POST /api/membership_points/find_by_dates?start_date=2024-05-12&end_date=2024-05-16&branch_id={Int32} /// Please pass in the 'branch_id' value for {Int32} in the above URL. /// internal static async void ExampleOfRequestForFindingMembershipPointsHistory() { long lAppID = 0L /* 这里需更改为贵公司使用的启网用户号 Fill in your Startnet user ID here */; string sAppKey = "这里需更改为贵公司使用的“AppKey” Fill in your 'AppKey' here"; string sApiDomain = "这里需更改为贵公司使用的 API 域名 Fill in your API domain here"; string sBranchName = "欲查找的机构(门店)名称 Branch name to search for"; // 【第一步】先查找出一个已存在于数据库的机构(门店)数据项目“Branch”。 // Step 1. Search for an existing 'Branch'. Branch oBranchInfo = await sBranchName.GetDataItem(lAppID, sAppKey, sApiDomain); int iBranchID; // 已成功找到数据项目 // Success if (oBranchInfo != null) { // 将仅查找属于该数据项目 ID 相应的机构(门店)下的会员卡积分历史发生情况数据。 // Search history data by the specified branch ID iBranchID = oBranchInfo.id; } // 找不到机构(门店)数据项目,或所调用的 API 已发生错误。 // No branch matched or an error has been raised. else { return; } // **************************************** // 就上面【第一步】提到的“iBranchID”变量值,也可从您本地服务器中已自 API 处同步的数据库副本中获取 // The value of variable 'iBranchID' in step 1 can also be obtained from synchronized data in your local database. // **************************************** // 【第二步】生成 API 请求以查找一个或多个已存在于数据库中的会员卡积分发生情况数据项目“MembershipPoints” // 这里的演示代码仅获取指定机构(门店)中最近一个月的数据,当然,日期范围和机构(门店)均可由您指定。 // Step 2. Make a request to search for one or more existing 'MembershipPoints'. // The demonstration here only obtains the data of the most recent month from the specified store or branch, of course, the date range and store or branch can be specified by you. DateTime oEndDate = DateTime.Now; DateTime oStartDate = oEndDate.AddMonths(-1); PagedResults oRspData = await $"find_by_dates".GetDataItems($"start_date={oStartDate:yyyy-MM-dd}&end_date={oEndDate:yyyy-MM-dd}&branch_id={iBranchID}", 1, lAppID, sAppKey, sApiDomain); // 已成功找到数据项目 // Success if ((oRspData?.results?.Count ?? 0) > 0) { System.Collections.Generic.List oMembershipPoints = oRspData.results; // 可继续在此处编写贵方的后续业务代码…… // Put your business code here to continue... } // 找不到任何匹配的数据项目,或所调用的 API 已发生错误。 // No data items found or an error has been raised. else { return; } } /// /// APIs:
/// POST /api/store_deliveries/find or, /api/store_take_deliveries/find
/// POST /api/store_deliveries/cancel or, /api/store_take_deliveries/cancel ///
internal static async void ExampleOfRequestForCancelStoreDeliveriesOrStoreTakeDeliveriesBill() { long lAppID = 0L /* Fill in your Startnet user ID here */; string sAppKey = "Fill in your 'AppKey' here"; string sApiDomain = "Fill in your API domain here"; string sBillCode = "Bill code to search for"; string sBillDate = "2024-06-25" /* Modify appropriately based on the actual bill date */; // **************************************** // Type 'StoreDeliveriesBill' in the following code can be replaced with 'StoreTakeDeliveriesBill' according to different businesses. // **************************************** // Step 1. Search for an existing 'StoreDeliveriesBill' or, 'StoreTakeDeliveriesBill'. string sDateRangeQuery = $"start_date={sBillDate.UrlEncode()}&end_date={sBillDate.UrlEncode()}"; StoreDeliveriesBill oBillInfo = await sBillCode.GetDataItem("Code", sDateRangeQuery, lAppID, sAppKey, sApiDomain); // Success if (oBillInfo != null) { // The bill has been cancelled and cannot be cancelled again. if (oBillInfo.is_cancelled) { return; } // If the bill has been approved, it cannot be cancel. else if (oBillInfo.review_operator_id > 0) { return; } } // No bill matched or an error has been raised. else { return; } // Step 2. Make a request to cancel a 'StoreDeliveriesBill'(MO) or, 'StoreTakeDeliveriesBill'(MI). // Call API to update data. // Kind 1: /api/store_deliveries/cancel // Kind 2: /api/store_take_deliveries/cancel StoreDeliveriesBill oNewlyData = await $"cancel".Operate(oBillInfo, lAppID, sAppKey, sApiDomain); // Success if (oNewlyData != null) { // Put your business code here to continue... } // Update failed or an error has been raised. else { return; } } // **************************************** // 以下代码中所涉及的实体类仅可在 4.1.0 或更高版本的 API 模型类库中使用。 // The following entity classes used are only supported in the model class library in version 4.1.0 or later. // **************************************** /// /// API: POST /api/retail_details_reports/v2/find_by_dates?start_date=2024-07-01&end_date=2024-07-02 /// internal static async void ExampleOfRequestForFindingRetailDetailsReport() { long lAppID = 0L /* 这里需更改为贵公司使用的启网用户号 Fill in your Startnet user ID here */; string sAppKey = "这里需更改为贵公司使用的“AppKey” Fill in your 'AppKey' here"; string sApiDomain = "这里需更改为贵公司使用的 API 域名 Fill in your API domain here"; // 生成 API 请求以查找一个或多个已存在于数据库中的零售明细数据项目“RetailDetailsReport2” // 这里的演示代码仅获取最近一个月的数据,当然,日期和时间范围可由您指定。 // Make a request to search for one or more existing 'RetailDetailsReport2'. // The demonstration here only obtains data from the most recent month, and of course, the date range can be specified by you. DateTime oEndDate = DateTime.Now; DateTime oStartDate = oEndDate.AddMonths(-1); PagedResults oRspData = await $"find_by_dates".GetDataItems($"start_date={oStartDate:yyyy-MM-dd}&end_date={oEndDate:yyyy-MM-dd}", 1, lAppID, sAppKey, sApiDomain); // 已成功找到数据项目 // Success if ((oRspData?.results?.Count ?? 0) > 0) { System.Collections.Generic.List oReports = oRspData.results; // 可继续在此处编写贵方的后续业务代码…… // Put your business code here to continue... } // 找不到任何匹配的数据项目,或所调用的 API 已发生错误。 // No data items found or an error has been raised. else { return; } } /// /// API: POST /api/inventories/v3 /// internal static async void ExampleOfRequestForFindingInventory() { long lAppID = 0L /* 这里需更改为贵公司使用的启网用户号 Fill in your Startnet user ID here */; string sAppKey = "这里需更改为贵公司使用的“AppKey” Fill in your 'AppKey' here"; string sApiDomain = "这里需更改为贵公司使用的 API 域名 Fill in your API domain here"; // 生成 API 请求以查找一个或多个已存在于数据库中的商品库存量数据项目“Inventory3” // 这里的演示代码仅获取指定代码对应门店的数据,当然,如果门店代码参数“hd_store_code”未指定或者为空,则会获取所有门店的数据。 // Make a request to search for one or more existing 'Inventory3'. // The demonstration here only retrieves data for the store corresponding to the specified code. // Of course, if the store code parameter 'hd_store_code' is not specified or empty, it will retrieve data for all stores. string sHdStoreCode = "指定海鼎门店代码,将只获取该门店下的商品库存量 The store code of Haiding to search for"; PagedResults oRspData = await GetDataItems($"", $"hd_store_code={sHdStoreCode.UrlEncode()}", 1, lAppID, sAppKey, sApiDomain); // 已成功找到数据项目 // Success if ((oRspData?.results?.Count ?? 0) > 0) { System.Collections.Generic.List oInventories = oRspData.results; // 可继续在此处编写贵方的后续业务代码…… // Put your business code here to continue... } // 找不到任何匹配的数据项目,或所调用的 API 已发生错误。 // No data items found or an error has been raised. else { return; } } // **************************************** // The following entity classes used are only supported in the model class library in version 4.5.0 or later. // **************************************** /// /// API: POST /api/sales_invoices/create /// internal static async void ExampleOfRequestForCreateSalesInvoiceBill() { long lAppID = 0L /* Fill in your Startnet user ID here */; string sAppKey = "Fill in your 'AppKey' here"; string sApiDomain = "Fill in your API domain here"; // Make a request of create a 'SalesInvoice'. var oPostData = new SalesInvoice { /* The 7 fields 'id', 'bill_code', 'bill_date', 'branch_id', 'customer_id', 'warehouse_id' and 'invoice_type' are required */ id = 0L /* Only set to 0 when adding a new bill data */, bill_code = "" /* Only set to the empty string when adding a new bill data */, bill_date = new DateTime(1900, 1, 1, 0, 0, 0) /* Only set to '1900-01-01T00:00:00' when adding a new bill data */, branch_id = 1, customer_id = 2, warehouse_id = 3, invoice_type = 3003 /* Set as 3001 or 3003 */, /* You can continue to set other fields as needed ... */ handler_id = 4 /* Set the handler ID */, custom_bill_code = "Set custom bill code", remark = "Set remark", details = new SalesInvoiceDetail[] { /* The price of the following product is specified by you */ new SalesInvoiceDetail() { /* The 4 fields 'id', 'bill_id', 'product_id' and 'quantity' are required */ id = 0L /* Only set to 0 when adding a new detail line data */, bill_id = 0L /* Only set to 0 when adding a new detail line data */, product_id = 1234, quantity = 10, /* The following 2 fields 'unit_price' and 'unit_price_tax' are required for customizing prices, otherwise they are optional */ unit_price = 987.654321m, unit_price_tax = 1187.654321m, /* You can continue to set other fields as needed ... */ memo = "Remarks for current detail line, or set to null value", }, /* The prices of the following 3 products are calculated by the system */ new SalesInvoiceDetail() { id = 0L, bill_id = 0L, product_id = 23456, quantity = 9 } /* Implicit assignment of null to fields 'unit_price' and 'unit_price_tax' */, new SalesInvoiceDetail() { id = 0L, bill_id = 0L, product_id = 56789, quantity = 8, unit_price = 0m, unit_price_tax = 0m, }, new SalesInvoiceDetail() { id = 0L, bill_id = 0L, product_id = 89012, quantity = 7, unit_price = null, unit_price_tax = null, }, /* You can continue to add more detail items as needed ... */ }, }; SalesInvoice oNewlySalesInvoice = await $"create".Operate(oPostData, lAppID, sAppKey, sApiDomain); // Successfully created sales invoice bill if (oNewlySalesInvoice != null) { // Put your business code here to continue... } // Failed to create sales invoice bill else { return; } } } }