SambaPOS API Integration with NewBook PMS/Booking System


#325

Cant believe I missed that one but using tasks for API logging, much better plan than the original discussed ages ago using txt files.

Note to self;

Using tasks.


#326

Hmmm, another thought, ticket log would be better place, tasks for api request runs, responces logged in ticket log, sounds ideal :slight_smile:… now though, do we have script helper for ticket log? if not maybe it would be good to go in to graphql if not already.


#327

I don’ think there is API for Ticket Log, But with Script and GraphQL, you have a lot of power over Tasks - much more than the single Add Task action that we have in Automation. GQL let’s you do just about anything with Tasks.

Both the KD and Timeclock in GQL Modules use Tasks and the power of GQL to manipulate them. It’s all done in Javascript code, which is pretty much the same as JScript in SambaPOS. All the GQL Queries and Mutations are available in Script via the gql.Exec() API command.


#328

Made an update to handle calculations.


var ticketDiscountIdList							= '';
	var ticketDiscountIdList							= ticketDiscountList(inputTicketId);																				//--query ticket discount total
	var ticketDiscountIdCount							= '';
	var ticketDiscountIdCount							= ticketDiscountCount(inputTicketId);
	if (ticketDiscountList) {																																				//--if discount defined
		for (d = 0; d < ticketDiscountIdCount ; d++) {
			var discountId								= ticketDiscountIdList[d];
			var discountDetailsArray					= ticketDiscountDetails(discountId).split('~');
			var discountName							= discountDetailsArray[0];
			var discountDecrease						= discountDetailsArray[1];
			var discountAmount							= discountDetailsArray[2];
			
			var discountGLAccountHashIndex				= discountName.indexOf('#');
			
			if (discountGLAccountHashIndex>-1){
				var discountGLAccountNumber				= discountName.slice(discountGLAccountHashIndex+1);
			} else {
				var discountGLAccountNumber				= discountGLAccount;
			}
			
			if (discountAmount<0){
				var discountValue							= discountAmount*-1;
			} else {
				var discountValue							= discountAmount*1;
			}
		//	return discountValue;
			var postDesctiption 						= 'Ticket: '+ticketNumberDescription+' - '+discountName;															//--built post description
			var postGLAccount	 						= discountGLAccountNumber;																							//--post gl_account code
			var postAmount 								= discountValue.toFixed(2);																							//--post amount to two d.places
			var postTaxFree 							= '0';																												//--post taxable number 0/1
			var postJsonData 							= new Object();																										//--define object for json post
			postJsonData.description 					= postDesctiption;																									//--json.description
			postJsonData.gl_account_code 				= postGLAccount;																									//--json.gl_account_code
			postJsonData.amount 						= postAmount;																										//--json.amount
			postJsonData.tax_free 						= postTaxFree;																										//--json.tax_free
			var postJson 								= JSON.stringify(postJsonData);																						//--stringify json object
		//	if(d==1){
		//		return discountDetailsArray[2];
		//	}
			if (discountDecrease==='True'){
				creditsArray							+= postJson+',';
			} else if(discountDecrease==='False'){
				chargesArray							+= postJson+',';																									//--add to credits variable
			}
		}
	}		//--end discount if

function ticketDiscountCount(inputTicketId) {																																//--Discounts on ticket by ticket id
	qry =  "SELECT COUNT([Id]) as [CT]										";
	qry	+= "FROM [Calculations]												";
	qry	+= "WHERE [TicketId] = '"+inputTicketId+"'							"	;																							//--QRY Variable + inputDate + inputPaymentType
	var discountCount = sql.Query(qry).First;																													//--SQL Query responce -> payment totals for work period exc payments
	return discountCount;																																					//--return discounts total
}	

function ticketDiscountList(inputTicketId) {																																//--Discounts on ticket by ticket id
	qry =  "SELECT [Id]														";
	qry	+= "FROM [Calculations]												";
	qry	+= "WHERE [TicketId] = '"+inputTicketId+"'							"	;																							//--QRY Variable + inputDate + inputPaymentType
	var discountList = sql.Query(qry).Delimit('~').All;																													//--SQL Query responce -> payment totals for work period exc payments
	return discountList;																																					//--return discounts total
}																																											//--end function

function ticketDiscountDetails(inputDiscountId) {																															//--Discounts on ticket by ticket id
	qry =  "SELECT [Name],[DecreaseAmount],[CalculationAmount]				";
	qry	+= "FROM [Calculations]												";
	qry	+= "WHERE [Id] = '"+inputDiscountId+"'"	;																															//--QRY Variable + inputDate + inputPaymentType
	var ticketDiscountDetails = sql.Query(qry).Delimit('~').First;																											//--SQL Query responce -> payment totals for work period exc payments
	return ticketDiscountDetails;																																			//--return discounts total
}							

Particularly like this bit;

var discountGLAccountHashIndex				= discountName.indexOf('#');
			
			if (discountGLAccountHashIndex>-1){
				var discountGLAccountNumber				= discountName.slice(discountGLAccountHashIndex+1);
			} else {
				var discountGLAccountNumber				= discountGLAccount;
			}

Which takes the number following the # on the calculation name to set which accounting department the charge/credit is created in NewBook.

So Regular Drinks Discount #2301 discount goes to GLAccount 2301 which is grouped under wet sales on 2101.
The if catches general other calculation should they get used for some reason like round and original discount.


#329

Exactly what I am looking for, but I don’t even know where to start :smile:


#330

Are you looking to use NewBook?
This all would give a near full intergration, you could get away with just posting room sales but personally prefer sending over all so there is a single point of reporting.
The current setup will have had some tweeks from scripts documented here. They also have updated API moving the request type to a value in the array with a single endpoint buy continuing old methods for now.
There could also be some improvements using the graphql calls to avoid some of the direct SQL scripts however the hotels here are still running a pre ql version of samba.


#331

I am looking for a solution that I can use for bookings and food sales at the same time. This post looks like it covered all that but it requires some time to grasp the flow. Is there a way we can use Samba itself without intergrating NewBook?


#332

The other room entities topic did in house rooms but not a booking calander for future bookings.
As I have told others if there are enough rooms and plan to use channels like booking.com etc a propper PMS like newbook is the way to go.
For a little B&B or pub with rooms could get by but even our 7 room pub/inn uses newbook and booking.com.
Booking.com etc a counts for arround 40% of our bookings up over 50% sometimes.
Plus online bookings direct? Is this wanted, newbook and other PMS will have this. You can use booking.coms sytem for lower fees on direct refered bookings I guess.

The company I work for is somehow linked to another system called the booking factory.
I pestered them for an API last year and they now have one and are much cheaper than newbook or at least were. There API is pretty basic compaired to newbook but they have the key room details and charging posts available.


#333

I think this other tutorial you did here will help me for now since the customer doesn’t have future bookings they just want to handle walk in bookings. I am going to try and implement it.