diff --git a/netsuitesdk/api/vendor_bills.py b/netsuitesdk/api/vendor_bills.py index 5153036..e92d182 100644 --- a/netsuitesdk/api/vendor_bills.py +++ b/netsuitesdk/api/vendor_bills.py @@ -29,34 +29,68 @@ def get_all_generator(self): def post(self, data) -> OrderedDict: assert data['externalId'], 'missing external id' vb = self.ns_client.VendorBill(externalId=data['externalId']) - expense_list = [] - for eod in data['expenseList']: - if 'customFieldList' in eod and eod['customFieldList']: - custom_fields = [] - for field in eod['customFieldList']: - if field['type'] == 'String': - custom_fields.append( - self.ns_client.StringCustomFieldRef( - scriptId=field['scriptId'] if 'scriptId' in field else None, - internalId=field['internalId'] if 'internalId' in field else None, - value=field['value'] + # If expesess are present, add them + if 'expenseList' in data and data['expenseList']: + expense_list = [] + for eod in data['expenseList']: + if 'customFieldList' in eod and eod['customFieldList']: + custom_fields = [] + for field in eod['customFieldList']: + if field['type'] == 'String': + custom_fields.append( + self.ns_client.StringCustomFieldRef( + scriptId=field['scriptId'] if 'scriptId' in field else None, + internalId=field['internalId'] if 'internalId' in field else None, + value=field['value'] + ) + ) + elif field['type'] == 'Select': + custom_fields.append( + self.ns_client.SelectCustomFieldRef( + scriptId=field['scriptId'] if 'scriptId' in field else None, + internalId=field['internalId'] if 'internalId' in field else None, + value=self.ns_client.ListOrRecordRef( + internalId=field['value'] + ) + ) ) - ) - elif field['type'] == 'Select': - custom_fields.append( - self.ns_client.SelectCustomFieldRef( - scriptId=field['scriptId'] if 'scriptId' in field else None, - internalId=field['internalId'] if 'internalId' in field else None, - value=self.ns_client.ListOrRecordRef( - internalId=field['value'] + eod['customFieldList'] = self.ns_client.CustomFieldList(custom_fields) + vbe = self.ns_client.VendorBillExpense(**eod) + expense_list.append(vbe) + + vb['expenseList'] = self.ns_client.VendorBillExpenseList(expense=expense_list) + + # If items are present, add them + if 'itemList' in data and data['itemList']: + item_list = [] + for eod in data['itemList']: + if 'customFieldList' in eod and eod['customFieldList']: + custom_fields = [] + for field in eod['customFieldList']: + print(field['value']) + if field['type'] == 'String': + custom_fields.append( + self.ns_client.StringCustomFieldRef( + scriptId=field['scriptId'] if 'scriptId' in field else None, + internalId=field['internalId'] if 'internalId' in field else None, + value=field['value'] ) ) - ) - eod['customFieldList'] = self.ns_client.CustomFieldList(custom_fields) - vbe = self.ns_client.VendorBillExpense(**eod) - expense_list.append(vbe) - - vb['expenseList'] = self.ns_client.VendorBillExpenseList(expense=expense_list) + elif field['type'] == 'Select': + custom_fields.append( + self.ns_client.SelectCustomFieldRef( + scriptId=field['scriptId'] if 'scriptId' in field else None, + internalId=field['internalId'] if 'internalId' in field else None, + value=self.ns_client.ListOrRecordRef( + internalId=field['value'] + ) + ) + ) + eod['customFieldList'] = self.ns_client.CustomFieldList(custom_fields) + vbe = self.ns_client.VendorBillItem(**eod) + item_list.append(vbe) + + vb['itemList']= self.ns_client.VendorBillItemList(item=item_list) if 'currency' in data: vb['currency'] = self.ns_client.RecordRef(**(data['currency'])) @@ -82,9 +116,6 @@ def post(self, data) -> OrderedDict: if 'account' in data: vb['account'] = self.ns_client.RecordRef(**(data['account'])) - if 'itemList' in data: - vb['itemList'] = data['itemList'] - if 'customFieldList' in data: vb['customFieldList'] = data['customFieldList'] diff --git a/setup.py b/setup.py index afaf535..faf6a9e 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name='netsuitesdk', - version='2.17.0', + version='2.17.1', author='Siva Narayanan', author_email='siva@fyle.in', description='Python SDK for accessing the NetSuite SOAP webservice', diff --git a/test/integration/data/vendor_bills/data_expense_and_items.json b/test/integration/data/vendor_bills/data_expense_and_items.json new file mode 100644 index 0000000..c46f13d --- /dev/null +++ b/test/integration/data/vendor_bills/data_expense_and_items.json @@ -0,0 +1,327 @@ +{ + "nullFieldList": null, + "createdDate": null, + "lastModifiedDate": null, + "nexus": null, + "subsidiaryTaxRegNum": null, + "taxRegOverride": null, + "taxDetailsOverride": null, + "customForm": null, + "billAddressList": null, + "account": { + "name": null, + "internalId": "25", + "externalId": null, + "type": "account" + }, + "entity": { + "name": null, + "internalId": "1674", + "externalId": null, + "type": "vendor" + }, + "subsidiary": { + "name": null, + "internalId": "3", + "externalId": null, + "type": "subsidiary" + }, + "location": { + "name": null, + "internalId": "8", + "externalId": null, + "type": "location" + }, + "approvalStatus": null, + "nextApprover": null, + "vatRegNum": null, + "postingPeriod": null, + "tranDate": "2023-05-11T07:06:21", + "currencyName": null, + "billingAddress": null, + "exchangeRate": null, + "entityTaxRegNum": null, + "taxPointDate": null, + "terms": null, + "dueDate": null, + "discountDate": null, + "tranId": null, + "userTotal": null, + "discountAmount": null, + "taxTotal": null, + "paymentHold": null, + "memo": null, + "tax2Total": null, + "creditLimit": null, + "availableVendorCredit": null, + "currency": { + "name": null, + "internalId": "1", + "externalId": null, + "type": "currency" + }, + "status": null, + "landedCostMethod": null, + "landedCostPerLine": null, + "transactionNumber": null, + "expenseList": [ + { + "orderDoc":null, + "orderLine":null, + "line":null, + "category":null, + "account":{ + "name":null, + "internalId":"82", + "externalId":null, + "type":"account" + }, + "amount":200.0, + "memo":"ashwin.t@fyle.in - Food - 2022-05-16 - C/2022/05/R/8 - ", + "grossAmt":200.0, + "taxDetailsReference":null, + "department":{ + "name":null, + "internalId":null, + "externalId":null, + "type":"department" + }, + "class":{ + "name":null, + "internalId":null, + "externalId":null, + "type":"classification" + }, + "location":{ + "name":null, + "internalId":null, + "externalId":null, + "type":"location" + }, + "customer":{ + "name":null, + "internalId":null, + "externalId":null, + "type":"customer" + }, + "customFieldList":[ + { + "scriptId":"custcolfyle_expense_url", + "type":"String", + "value":"https://localhost:8000/api/app/main/#/enterprise/view_expense/tx4Suhvjafbd?org_id=orpMfWO2KOsU" + }, + { + "scriptId":"custcol788", + "type":"Select", + "value":"1" + } + ], + "isBillable":null, + "projectTask":null, + "tax1Amt":null, + "taxAmount":null, + "taxCode":{ + "name":null, + "internalId":null, + "externalId":null, + "type":"taxGroup" + }, + "taxRate1":null, + "taxRate2":null, + "amortizationSched":null, + "amortizStartDate":null, + "amortizationEndDate":null, + "amortizationResidual":null + }, + { + "orderDoc":null, + "orderLine":null, + "line":null, + "category":null, + "account":{ + "name":null, + "internalId":"89", + "externalId":null, + "type":"account" + }, + "amount":200.0, + "memo":"ashwin.t@fyle.in - Food labhvam - 2022-05-16 - C/2022/05/R/8 - ", + "grossAmt":200.0, + "taxDetailsReference":null, + "department":{ + "name":null, + "internalId":null, + "externalId":null, + "type":"department" + }, + "class":{ + "name":null, + "internalId":null, + "externalId":null, + "type":"classification" + }, + "location":{ + "name":null, + "internalId":null, + "externalId":null, + "type":"location" + }, + "customer":{ + "name":null, + "internalId":null, + "externalId":null, + "type":"customer" + }, + "isBillable":null, + "projectTask":null, + "tax1Amt":null, + "taxAmount":null, + "taxCode":{ + "name":null, + "internalId":null, + "externalId":null, + "type":"taxGroup" + }, + "customFieldList":[ + { + "scriptId":"custcolfyle_expense_url", + "type":"String", + "value":"https://localhost:8000/api/app/main/#/enterprise/view_expense/tx4Suhvjafbd?org_id=orpMfWO2KOsU" + } + ], + "taxRate1":null, + "taxRate2":null, + "amortizationSched":null, + "amortizStartDate":null, + "amortizationEndDate":null, + "amortizationResidual":null + } + ], + "itemList": [ + { + "item": { + "name": null, + "internalId": "504", + "externalId": null, + "type": null + }, + "line": null, + "vendorName": null, + "orderDoc": null, + "orderLine": null, + "quantity": 1.0, + "units": null, + "inventoryDetail": null, + "description": "custom thing", + "serialNumbers": null, + "binNumbers": null, + "expirationDate": null, + "taxCode": null, + "taxRate1": null, + "taxRate2": null, + "grossAmt": null, + "tax1Amt": null, + "rate": "71.00", + "amount": 71.0, + "options": null, + "department": null, + "class": null, + "location": { + "name": null, + "internalId": "8", + "externalId": null, + "type": null + }, + "customer": null, + "customFieldList":[ + { + "scriptId":"custcolfyle_expense_url", + "type":"String", + "value":"https://localhost:8000/api/app/main/#/enterprise/view_expense/tx4Suhvjafbd?org_id=orpMfWO2KOsU" + }, + { + "scriptId":"custcol788", + "type":"Select", + "value":"1" + } + ], + "landedCostCategory": null, + "isBillable": null, + "billVarianceStatus": null, + "billreceiptsList": null, + "amortizationSched": null, + "amortizStartDate": null, + "amortizationEndDate": null, + "amortizationResidual": null, + "taxAmount": null, + "taxDetailsReference": null, + "landedCost": null + }, + { + "item": { + "name": null, + "internalId": "387", + "externalId": null, + "type": null + }, + "line": null, + "vendorName": null, + "orderDoc": null, + "orderLine": null, + "quantity": 1.0, + "units": null, + "inventoryDetail": null, + "description": "custom thing", + "serialNumbers": null, + "binNumbers": null, + "expirationDate": null, + "taxCode": null, + "taxRate1": null, + "taxRate2": null, + "grossAmt": null, + "tax1Amt": null, + "rate": "101.00", + "amount": 101.0, + "options": null, + "department": null, + "class": null, + "location": { + "name": null, + "internalId": "8", + "externalId": null, + "type": null + }, + "customer": null, + "customFieldList":[ + { + "scriptId":"custcolfyle_expense_url", + "type":"String", + "value":"https://localhost:8000/api/app/main/#/enterprise/view_expense/tx4Suhvjafbd?org_id=orpMfWO2KOsU" + }, + { + "scriptId":"custcol788", + "type":"Select", + "value":"1" + } + ], + "landedCostCategory": null, + "isBillable": null, + "billVarianceStatus": null, + "billreceiptsList": null, + "amortizationSched": null, + "amortizStartDate": null, + "amortizationEndDate": null, + "amortizationResidual": null, + "taxAmount": null, + "taxDetailsReference": null, + "landedCost": null + } + ], + "accountingBookDetailList": null, + "landedCostsList": null, + "purchaseOrderList": null, + "taxDetailsList": null, + "customFieldList": null, + "internalId": null, + "externalId": "G/2022/05/R/rick" +} \ No newline at end of file diff --git a/test/integration/data/vendor_bills/data.json b/test/integration/data/vendor_bills/data_expenses_only.json similarity index 100% rename from test/integration/data/vendor_bills/data.json rename to test/integration/data/vendor_bills/data_expenses_only.json diff --git a/test/integration/data/vendor_bills/data_items_only.json b/test/integration/data/vendor_bills/data_items_only.json new file mode 100644 index 0000000..f270d28 --- /dev/null +++ b/test/integration/data/vendor_bills/data_items_only.json @@ -0,0 +1,194 @@ +{ + "nullFieldList": null, + "createdDate": null, + "lastModifiedDate": null, + "nexus": null, + "subsidiaryTaxRegNum": null, + "taxRegOverride": null, + "taxDetailsOverride": null, + "customForm": null, + "billAddressList": null, + "account": { + "name": null, + "internalId": "25", + "externalId": null, + "type": "account" + }, + "entity": { + "name": null, + "internalId": "1674", + "externalId": null, + "type": "vendor" + }, + "subsidiary": { + "name": null, + "internalId": "3", + "externalId": null, + "type": "subsidiary" + }, + "location": { + "name": null, + "internalId": "8", + "externalId": null, + "type": "location" + }, + "approvalStatus": null, + "nextApprover": null, + "vatRegNum": null, + "postingPeriod": null, + "tranDate": "2023-05-11T07:06:21", + "currencyName": null, + "billingAddress": null, + "exchangeRate": null, + "entityTaxRegNum": null, + "taxPointDate": null, + "terms": null, + "dueDate": null, + "discountDate": null, + "tranId": null, + "userTotal": null, + "discountAmount": null, + "taxTotal": null, + "paymentHold": null, + "memo": null, + "tax2Total": null, + "creditLimit": null, + "availableVendorCredit": null, + "currency": { + "name": null, + "internalId": "1", + "externalId": null, + "type": "currency" + }, + "status": null, + "landedCostMethod": null, + "landedCostPerLine": null, + "transactionNumber": null, + "itemList": [ + { + "item": { + "name": null, + "internalId": "504", + "externalId": null, + "type": null + }, + "line": null, + "vendorName": null, + "orderDoc": null, + "orderLine": null, + "quantity": 1.0, + "units": null, + "inventoryDetail": null, + "description": "custom thing", + "serialNumbers": null, + "binNumbers": null, + "expirationDate": null, + "taxCode": null, + "taxRate1": null, + "taxRate2": null, + "grossAmt": null, + "tax1Amt": null, + "rate": "71.00", + "amount": 71.0, + "options": null, + "department": null, + "class": null, + "location": { + "name": null, + "internalId": "8", + "externalId": null, + "type": null + }, + "customer": null, + "customFieldList":[ + { + "scriptId":"custcolfyle_expense_url", + "type":"String", + "value":"https://localhost:8000/api/app/main/#/enterprise/view_expense/tx4Suhvjafbd?org_id=orpMfWO2KOsU" + }, + { + "scriptId":"custcol788", + "type":"Select", + "value":"1" + } + ], + "landedCostCategory": null, + "isBillable": null, + "billVarianceStatus": null, + "billreceiptsList": null, + "amortizationSched": null, + "amortizStartDate": null, + "amortizationEndDate": null, + "amortizationResidual": null, + "taxAmount": null, + "taxDetailsReference": null, + "landedCost": null + }, + { + "item": { + "name": null, + "internalId": "387", + "externalId": null, + "type": null + }, + "line": null, + "vendorName": null, + "orderDoc": null, + "orderLine": null, + "quantity": 1.0, + "units": null, + "inventoryDetail": null, + "description": "custom thing", + "serialNumbers": null, + "binNumbers": null, + "expirationDate": null, + "taxCode": null, + "taxRate1": null, + "taxRate2": null, + "grossAmt": null, + "tax1Amt": null, + "rate": "101.00", + "amount": 101.0, + "options": null, + "department": null, + "class": null, + "location": { + "name": null, + "internalId": "8", + "externalId": null, + "type": null + }, + "customer": null, + "customFieldList":[ + { + "scriptId":"custcolfyle_expense_url", + "type":"String", + "value":"https://localhost:8000/api/app/main/#/enterprise/view_expense/tx4Suhvjafbd?org_id=orpMfWO2KOsU" + }, + { + "scriptId":"custcol788", + "type":"Select", + "value":"1" + } + ], + "landedCostCategory": null, + "isBillable": null, + "billVarianceStatus": null, + "billreceiptsList": null, + "amortizationSched": null, + "amortizStartDate": null, + "amortizationEndDate": null, + "amortizationResidual": null, + "taxAmount": null, + "taxDetailsReference": null, + "landedCost": null + } + ], + "accountingBookDetailList": null, + "landedCostsList": null, + "purchaseOrderList": null, + "taxDetailsList": null, + "customFieldList": null, + "internalId": null, + "externalId": "G/2022/05/R/tims" +} \ No newline at end of file diff --git a/test/integration/test_vendor_bills.py b/test/integration/test_vendor_bills.py index 643b216..3141638 100644 --- a/test/integration/test_vendor_bills.py +++ b/test/integration/test_vendor_bills.py @@ -17,7 +17,7 @@ def test_get(nc): def test_post(nc): vb1 = {} - with open('./test/integration/data/vendor_bills/data.json') as oj: + with open('./test/integration/data/vendor_bills/data_expenses_only.json') as oj: s = oj.read() vb1 = json.loads(s) logger.debug('rvb1 = %s', vb1) @@ -29,3 +29,22 @@ def test_post(nc): logger.debug('vb2 = %s', vb2) assert (29.99 < vb2['userTotal']) and (vb2['userTotal'] < 30.01), 'Bill total is not 30.0' + + vb3 = {} + with open('./test/integration/data/vendor_bills/data_items_only.json') as oj: + s = oj.read() + vb3 = json.loads(s) + logger.debug('rvb1 = %s', vb3) + res = nc.vendor_bills.post(vb3) + logger.debug('res = %s', res) + assert res['externalId'] == vb3['externalId'], 'External ID does not match' + + + vb4 = {} + with open('./test/integration/data/vendor_bills/data_expense_and_items.json') as oj: + s = oj.read() + vb4 = json.loads(s) + logger.debug('rvb1 = %s', vb4) + res = nc.vendor_bills.post(vb4) + logger.debug('res = %s', res) + assert res['externalId'] == vb4['externalId'], 'External ID does not match' \ No newline at end of file