1import {logging} from "../../imaginary/util/logging.js"; 2import {locale} from "../../imaginary/util/i18n.js"; 3import {Factory, Model} from "../../imaginary/sys/model.js"; 4import {LocalizedDateTime} from "../../imaginary/util/dates.js"; 5import {MembershipTier} from "./tiers.js"; 6import {Site} from "../../imaginary/site.js"; 7import {Product} from "./product.js"; 8import {DataTools} from "../../imaginary/util/data.js"; 9import {RestCall} from "../../imaginary/util/rest.js"; 10import {authentication} from "../../imaginary/sys/idm.js"; 11 12 13const logger = logging.logger('mnwf.ecommerce.ticket'); 14 15 16export class Ticket extends Model { 17 static get apiNamespace() { 18 return 'festival'; 19 } 20 21 static get apiVersion() { 22 return 2; 23 } 24 25 static async create(editionId, payload) { 26 const start = logger.enter('create()'); 27 try { 28 if( !editionId ) { 29 editionId = window.application.selectedEdition.editionId; 30 } 31 logger.info('Creating a new ticket in ' + editionId); 32 if( logger.isDebug() ) { 33 console.log(payload); 34 } 35 const id = await RestCall.create(Ticket.apiVersion, Ticket.apiNamespace, `edition/${editionId}/finance/merchandise/ticket`, payload); 36 37 logger.info('Created new ticket: ' + id); 38 return await _factory.created(id); 39 } 40 finally { 41 logger.exit('create()', start); 42 } 43 } 44 45 static async mapNames(items, field) { 46 let names = []; 47 48 if( items && items.length > 0 ) { 49 for( let i = 0; i < items.length; i++ ) { 50 const title = (await items[i])[field]; 51 52 if( title ) { 53 const name = locale.translate(title); 54 55 if( !names.includes(name) ) { 56 names.push(name); 57 } 58 } 59 } 60 } 61 return names; 62 } 63 64 static async myTicket(ticketId, reload) { 65 if( authentication.authenticated ) { 66 const state = await RestCall.call(Ticket.apiVersion, Ticket.apiNamespace, `me/tickets/${ticketId}`, 'ticket', reload); 67 68 if( !state ) { 69 return null; 70 } 71 return new Ticket(state); 72 } 73 return []; 74 } 75 76 static async ticket(ticketId, reload=false, deep=false) { 77 const ticket = await _factory.item(ticketId, null, reload); 78 const pp = ticket.product; 79 80 if( deep ) { 81 await pp; 82 } 83 return ticket; 84 } 85 86 static async tickets(editionId=null, reload=false) { 87 const tickets = await _factory.items(null, reload); 88 89 if( !editionId ) { 90 return tickets; 91 } 92 return tickets.filter( (t) => t.editionId === editionId ); 93 } 94 95 static async ticketsEntitledTo(editionId, entitlement, itemId) { 96 const tickets = await Ticket.tickets(editionId); 97 98 return tickets.filter( (t) => { 99 const entitlements = t.entitlements?.[entitlement]; 100 101 if( DataTools.isEmpty(entitlements) ) { 102 return false; 103 } 104 return entitlements.includes('*') || entitlements.includes(itemId); 105 }); 106 } 107 108 static ticketStream(editionId=null, reload=false) { 109 let f = null; 110 111 if( editionId ) { 112 f = (t) => { 113 return t.editionId === editionId; 114 }; 115 } 116 return _factory.stream(null, reload, f); 117 } 118 119 constructor(state) { 120 super(state); 121 } 122 123 get active() { 124 return this.state['active']; 125 } 126 127 get capacity() { 128 return this.state['capacity']; 129 } 130 131 get constraints() { 132 return this.state['usage_constraints']; 133 } 134 135 get editionId() { 136 return this.state['group_id']; 137 } 138 get entitlements() { 139 return this.state['entitlements']; 140 } 141 142 get groupId() { 143 return this.state['group_id']; 144 } 145 146 /** @private **/ 147 async _issues() { 148 return []; 149 } 150 151 get issues() { 152 return this._issues(); 153 } 154 155 get onSale() { 156 const start = this.saleStart; 157 const end = this.saleEnd; 158 159 if( !start && !end ) { 160 return true; 161 } 162 const now = LocalizedDateTime.now(); 163 164 if( start && start.after(now) ) { 165 return false;
166 } 167 return !(end && end.before(now)); 168 } 169 170 async _pricing() { 171 const product = await this.product; 172 173 if( product ) { 174 const pricing = await product.loadPricing('all'); 175 176 if( pricing ) { 177 return pricing; 178 } 179 } 180 return null; 181 } 182 183 async _price() { 184 const level = await MembershipTier.defaultTier(); 185 186 if( !level ) { 187 return null; 188 } 189 const pricing = await this._pricing(); 190 191 if( !pricing ) { 192 return null; 193 } 194 if( !pricing['pricing'][level.membershipTierId] ) { 195 return null; 196 } 197 return pricing['pricing'][level.membershipTierId][Site.defaultCurrency]; 198 } 199 200 get price() { 201 return this._price(); 202 } 203 204 async priceFor(tierId) { 205 if( !tierId ) { 206 return null; 207 } 208 const pricing = await this._pricing(); 209 210 if( !pricing ) { 211 return null; 212 } 213 if( !pricing['pricing'][tierId] ) { 214 return null; 215 } 216 return pricing['pricing'][tierId][Site.defaultCurrency]; 217 } 218 219 get pricing() { 220 return this._pricing(); 221 } 222 223 async getProduct(allowCaching=false) { 224 return this._product(allowCaching); 225 } 226 227 async _product(allowCaching) { 228 return Product.productFor('ticket', this.ticketId, !allowCaching); 229 } 230 231 get product() { 232 return this._product(false); 233 } 234 235 async remove() { 236 const start = logger.enter('remove()'); 237 try { 238 logger.info('Deleting ticket: ' + this.ticketId); 239 240 await RestCall.delete(this.apiVersionIdentifier, this.apiNamespaceIdentifier, `${this.apiResourceTarget}/${this.ticketId}`); 241 _factory.removed(this.ticketId); 242 logger.info('Ticket successfully deleted'); 243 } 244 finally { 245 logger.exit('remove()', start); 246 } 247 } 248 249 get saleEnd() { 250 if( !this.state['sale_end'] ) { 251 return null; 252 } 253 return new LocalizedDateTime(this.state['sale_end']); 254 } 255 256 get saleStart() { 257 if( !this.state['sale_start'] ) { 258 return null; 259 } 260 return new LocalizedDateTime(this.state['sale_start']); 261 } 262 263 async save(payload) { 264 const start = logger.enter('save()'); 265 try { 266 logger.info('Saving changes to ' + this.ticketId); 267 if( logger.isDebug() ) { 268 console.log(payload); 269 } 270 payload['ts_modified'] = this.state['ts_modified']; 271 await RestCall.update(Ticket.apiVersion, Ticket.apiNamespace, `finance/merchandise/ticket/${this.ticketId}`, payload); 272 await Ticket.ticket(this.ticketId, true); 273 274 logger.info('Ticket update was successful'); 275 if( this._state['product'] ) { 276 this._state['product'] = null; 277 } 278 } 279 finally { 280 logger.exit('save()', start); 281 } 282 } 283 284 get soldOut() { 285 return this.state['sold_out']; 286 } 287 288 get stale() { 289 return this.checkStale(`finance/merchandise/ticket`); 290 } 291 292 get synopsis() { 293 return this.state['synopsis']; 294 } 295 296 get ticketId() { 297 return this.state['id']; 298 } 299 300 get title() { 301 return this.state['title']; 302 } 303 304 touch() { 305 this._state['product']?.touch(); 306 super.touch(); 307 } 308 309 get tsCreated() { 310 return this.state['ts_created']; 311 } 312 313 get tsModified() { 314 return this.state['ts_modified']; 315 } 316} 317 318 319const _factory = Factory.factory(Ticket, Ticket.apiVersion, Ticket.apiNamespace,'finance/merchandise/ticket', 'ticket', 'id'); 320
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.