PageSourceSearch

https://mnwebfest.com/_modules/mnwf/ecommerce/ticket.js

js mnwebfest.com collected 2026-10-02 16:26:28 UTC 8,126 bytes, 320 lines download raw bytes

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.