Skip to content

Commit

Permalink
Netsuite SDK support for Journal Entries
Browse files Browse the repository at this point in the history
  • Loading branch information
Sravanksk authored Jul 10, 2020
1 parent d5e7960 commit 874b31e
Show file tree
Hide file tree
Showing 9 changed files with 318 additions and 3 deletions.
54 changes: 54 additions & 0 deletions netsuitesdk/api/journal_entries.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from collections import OrderedDict
from netsuitesdk.internal.utils import PaginatedSearch

from .base import ApiBase
import logging

logger = logging.getLogger(__name__)


class JournalEntries(ApiBase):
def __init__(self, ns_client):
ApiBase.__init__(self, ns_client=ns_client, type_name='journalEntry')

def get_all_generator(self):
record_type_search_field = self.ns_client.SearchStringField(searchValue='JournalEntry', operator='contains')
basic_search = self.ns_client.basic_search_factory('Transaction', recordType=record_type_search_field)
paginated_search = PaginatedSearch(client=self.ns_client,
type_name='Transaction',
basic_search=basic_search,
pageSize=20)
return self._paginated_search_to_generator(paginated_search=paginated_search)

def post(self, data) -> OrderedDict:
assert data['externalId'], 'missing external id'
je = self.ns_client.JournalEntry(externalId=data['externalId'])
line_list = []
for eod in data['lineList']:
jee = self.ns_client.JournalEntryLine(**eod)
line_list.append(jee)

je['lineList'] = self.ns_client.JournalEntryLineList(line=line_list)
je['currency'] = self.ns_client.RecordRef(**(data['currency']))

if 'memo' in data:
je['memo'] = data['memo']

if 'tranId' in data:
je['tranId'] = data['tranId']

if 'subsidiary' in data:
je['subsidiary'] = data['subsidiary']

if 'class' in data:
je['class'] = data['class']

if 'location' in data:
je['location'] = data['location']

if 'department' in data:
je['department'] = data['department']

logger.debug('able to create je = %s', je)
res = self.ns_client.upsert(je)
return self._serialize(res)
2 changes: 2 additions & 0 deletions netsuitesdk/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .api.vendor_bills import VendorBills
from .api.vendors import Vendors
from .api.subsidiaries import Subsidiaries
from .api.journal_entries import JournalEntries
from .internal.client import NetSuiteClient


Expand All @@ -26,3 +27,4 @@ def __init__(self, account, consumer_key, consumer_secret, token_key, token_secr
self.vendor_bills = VendorBills(ns_client)
self.vendors = Vendors(ns_client)
self.subsidiaries = Subsidiaries(ns_client)
self.journal_entries = JournalEntries(ns_client)
8 changes: 8 additions & 0 deletions netsuitesdk/internal/netsuite_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@
'VendorBillItemList',
'VendorPayment',
],

# urn:general_2019_2.transactions.webservices.netsuite.com
# https://webservices.netsuite.com/xsd/transactions/v2019_2_0/general.xsd
'ns31': [
'JournalEntry',
'JournalEntryLine',
'JournalEntryLineList',
],
}

SIMPLE_TYPES = {
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name='netsuitesdk',
version='1.4.0',
version='1.5.0',
author='Siva Narayanan',
author_email='[email protected]',
description='Python SDK for accessing the NetSuite SOAP webservice',
Expand Down
154 changes: 154 additions & 0 deletions test/integration/data/journal_entries/tstdrv2089588.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
{
"accountingBook": null,
"accountingBookDetailList": null,
"approved": null,
"class": null,
"createdDate": null,
"createdFrom": null,
"currency": {
"name": "USD",
"internalId": null,
"externalId": null,
"type": "currency"
},
"customFieldList": null,
"customForm": null,
"department": null,
"exchangeRate": null,
"isBookSpecific": null,
"lastModifiedDate": null,
"lineList": [
{
"account": {
"name": null,
"internalId": "25",
"externalId": null,
"type": "account"
},
"department": {
"name": null,
"internalId": "10",
"externalId": null,
"type": "department"
},
"location": {
"name": null,
"internalId": "2",
"externalId": null,
"type": "location"
},
"class": {
"name": null,
"internalId": null,
"externalId": null,
"type": "account"
},
"entity": {
"name": null,
"internalId": "1648",
"externalId": null,
"type": "vendor"
},
"credit": 500,
"creditTax": null,
"customFieldList": null,
"debit": null,
"debitTax": null,
"eliminate": null,
"endDate": null,
"grossAmt": null,
"line": null,
"lineTaxCode": null,
"lineTaxRate": null,
"memo": "Testing JournalEntry via Fyle SDK 3",
"residual": null,
"revenueRecognitionRule": null,
"schedule": null,
"scheduleNum": null,
"startDate": null,
"tax1Acct": null,
"tax1Amt": null,
"taxAccount": null,
"taxBasis": null,
"taxCode": null,
"taxRate1": null,
"totalAmount": null
},
{
"account": {
"name": null,
"internalId": "25",
"externalId": null,
"type": "account"
},
"department": {
"name": null,
"internalId": "10",
"externalId": null,
"type": "department"
},
"location": {
"name": null,
"internalId": "2",
"externalId": null,
"type": "location"
},
"class": {
"name": null,
"internalId": null,
"externalId": null,
"type": "account"
},
"credit": null,
"creditTax": null,
"customFieldList": null,
"debit": 500,
"debitTax": null,
"eliminate": null,
"endDate": null,
"entity": {
"name": null,
"internalId": "1648",
"externalId": null,
"type": "vendor"
},
"grossAmt": null,
"line": null,
"lineTaxCode": null,
"lineTaxRate": null,
"memo": "Testing JournalEntry via Fyle SDK 3",
"residual": null,
"revenueRecognitionRule": null,
"schedule": null,
"scheduleNum": null,
"startDate": null,
"tax1Acct": null,
"tax1Amt": null,
"taxAccount": null,
"taxBasis": null,
"taxCode": null,
"taxRate1": null,
"totalAmount": null
}
],
"location": null,
"memo": "JE Testing Fyle SDK 3",
"nexus": null,
"parentExpenseAlloc": null,
"postingPeriod": null,
"reversalDate": null,
"reversalDefer": null,
"reversalEntry": null,
"subsidiary": {
"name": null,
"internalId": "1",
"externalId": null,
"type": "subsidiary"
},
"subsidiaryTaxRegNum": null,
"taxPointDate": null,
"toSubsidiary": null,
"tranDate": null,
"tranId": null,
"externalId": "JE_04"
}
39 changes: 39 additions & 0 deletions test/integration/test_journal_entries.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import logging
import pytest
import json
import os

logger = logging.getLogger(__name__)

def test_get(nc):
data = next(nc.journal_entries.get_all_generator())
logger.debug('data = %s', data)
assert data, 'get all generator didnt work'
assert data['internalId'] == '16', f'No object found with internalId'

data = nc.journal_entries.get(externalId='JE_04')
logger.debug('data = %s', data)
currency = data['currency']
assert data, f'No object with externalId'
assert data['internalId'] == '10512', f'No object with internalId'
assert data['externalId'] == 'JE_04', f'No object with externalId'
assert currency['name'] == 'USA', f'Currency does not match'

def test_post(nc):
filename = os.getenv('NS_ACCOUNT').lower() + '.json'
with open('./test/integration/data/journal_entries/' + filename) as oj:
s = oj.read()
je1 = json.loads(s)
logger.debug('rvb1 = %s', je1)
res = nc.journal_entries.post(je1)
logger.debug('res = %s', res)
assert res['externalId'] == je1['externalId'], 'External ID does not match'
assert res['type'] == 'journalEntry', 'Type does not match'

je2 = nc.journal_entries.get(externalId=res['externalId'])
currency = je2['currency']
assert je2['internalId'] == '10512', f'No object with internalId'
assert je2['externalId'] == 'JE_04', f'No object with externalId'
assert currency['name'] == 'USA', f'Currency does not match'

logger.debug('je2 = %s', je2)
5 changes: 5 additions & 0 deletions test/internal/test_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ def test_get_vendor_bill(ns):
record = ns.get(recordType='vendorBill', externalId='1234')
assert record, 'No vendor bill found'


def test_get_journal_entry(ns):
record = ns.get(recordType='journalEntry', externalId='JE_01')
assert record, 'No journal entry found'

# def test_get_currency1(nc):
# currency = nc.currency.get(internal_id='1')
# logger.info('currency is %s', currency)
10 changes: 10 additions & 0 deletions test/internal/test_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ def test_search_vendor_bills(ns):
assert len(paginated_search.records) > 0, 'There are no vendor bills'
logger.debug('record = %s', str(paginated_search.records[0]))

def test_search_journal_entries(ns):
record_type_search_field = ns.SearchStringField(searchValue='JournalEntry', operator='contains')
basic_search = ns.basic_search_factory('Transaction', recordType=record_type_search_field)
paginated_search = PaginatedSearch(client=ns,
type_name='Transaction',
basic_search=basic_search,
pageSize=5)
assert len(paginated_search.records) > 0, 'There are no journal entries'
logger.debug('record = %s', str(paginated_search.records[0]))

@pytest.mark.parametrize('type_name', ['Account', 'Vendor', 'Department', 'Location', 'Classification'])
def test_search_all(ns, type_name):
paginated_search = PaginatedSearch(client=ns, type_name=type_name, pageSize=20)
Expand Down
47 changes: 45 additions & 2 deletions test/internal/test_upsert.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#@pytest.mark.parametrize('type_name', ['Account', 'Vendor', 'Department', 'Location', 'Classification'])
def get_record(ns, type_name):
paginated_search = PaginatedSearch(client=ns, type_name=type_name, pageSize=20)
return paginated_search.records[0]
return paginated_search.records[0]

def get_location(ns):
return get_record(ns, 'Location')
Expand All @@ -23,7 +23,7 @@ def get_vendor(ns):
return get_record(ns, 'Vendor')

def get_category_account(ns):
return ns.get(recordType='account', internalId=68)
return ns.get(recordType='account', internalId=84)

def get_currency(ns):
return ns.get(recordType='currency', internalId='1')
Expand Down Expand Up @@ -70,3 +70,46 @@ def test_upsert_vendor_bill(ns):
bill2 = ns.get(recordType='vendorBill', externalId='1234')
logger.debug('bill2 = %s', str(bill2))
assert (29.99 < bill2['userTotal']) and (bill2['userTotal'] < 30.01), 'Bill total is not 30.0'

def test_upsert_journal_entry(ns):
vendor_ref = ns.RecordRef(type='vendor', internalId=get_vendor(ns).internalId)
cat_account_ref = ns.RecordRef(type='account', internalId=get_category_account(ns).internalId)
loc_ref = ns.RecordRef(type='location', internalId=get_location(ns).internalId)
dep_ref = ns.RecordRef(type='department', internalId=get_department(ns).internalId)
class_ref = ns.RecordRef(type='classification', internalId=get_department(ns).internalId)
lines = []

credit_line = ns.JournalEntryLine()
credit_line['account'] = cat_account_ref
credit_line['department'] = dep_ref
credit_line['class'] = class_ref
credit_line['location'] = loc_ref
credit_line['entity'] = vendor_ref
credit_line['credit'] = 20.0

lines.append(credit_line)

debit_line = ns.JournalEntryLine()
debit_line['account'] = cat_account_ref
debit_line['department'] = dep_ref
debit_line['class'] = class_ref
debit_line['location'] = loc_ref
debit_line['entity'] = vendor_ref
debit_line['debit'] = 20.0

lines.append(debit_line)

journal_entry = ns.JournalEntry(externalId='JE_1234')
journal_entry['currency'] = ns.RecordRef(type='currency', internalId=get_currency(ns).internalId) # US dollar
journal_entry['subsidiary'] = ns.RecordRef(type='subsidiary', internalId='1')
journal_entry['exchangerate'] = 1.0
journal_entry['lineList'] = ns.JournalEntryLineList(line=lines)
journal_entry['memo'] = 'test memo'
logger.debug('upserting journal entry %s', journal_entry)
record_ref = ns.upsert(journal_entry)
logger.debug('record_ref = %s', record_ref)
assert record_ref['externalId'] == 'JE_1234', 'External ID does not match'

je = ns.get(recordType='journalEntry', externalId='JE_1234')
logger.debug('je = %s', str(je))
assert (je['externalId'] == 'JE_1234'), 'Journal Entry External ID does not match'

0 comments on commit 874b31e

Please sign in to comment.