Guest User

Untitled

a guest
Apr 23rd, 2018
113
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.79 KB | None | 0 0
  1. import * as Sequelize from 'sequelize'
  2. import { BaseModel, SequelizeModels } from './modelTypes'
  3. import {Moment} from "moment";
  4. import * as moment from 'moment'
  5. import { LocationGI } from './location'
  6. import { SurveyGI} from './survey'
  7. import { ServiceOptionGI } from './serviceOption'
  8. import { ServiceCategoryGI } from './serviceCategory'
  9. import { ResourceTypeGI } from './resourceType'
  10. import { PriceRuleGI } from './priceRule'
  11. import { ServiceResourceTypeLinkGA } from './serviceResourceTypeLink'
  12. import { ResourceGI } from './resource'
  13. import { ServiceResourceLinkGA } from './serviceResourceLink'
  14. import { NotificationTypeGI } from './notificationType'
  15. import { BookingOptionGI } from './bookingOption'
  16. import db from './index'
  17. import { Availability } from '../lib/availability'
  18. import { IAvailabilityConfig } from '../lib/availability/types'
  19. import { getI18nTitle, localizationMixin, i18nUpdate } from '../lib/i18n'
  20. import { PriceNotFound } from '../lib/errors/entities/service'
  21.  
  22. export interface ServiceGA extends BaseModel{
  23. name? : string;
  24. deletedAt?: Moment;
  25. type?: ServiceTypeEnum;
  26. working_hours?: string;
  27. non_available_schedule?: string;
  28. min_duration?: number;
  29. max_duration?: number;
  30. default_duration?: number;
  31. time_gap?: number;
  32. min_booking_interval?: number;
  33. max_booking_interval?: number;
  34. check_in_time?: number;
  35. check_out_time?: number;
  36. NotificationTypes?: NotificationTypeGI[];
  37. location_id?: number;
  38. Survey?: SurveyGI;
  39. ServiceOptions?: ServiceOptionGI[];
  40. ServiceCategories?: ServiceCategoryGI[];
  41. ResourceTypes?: ResourceTypeGI[];
  42. Resources?: ResourceGI[];
  43. BookingOptions?: BookingOptionGI[];
  44. slot_duration?: number;
  45. cancel_gap?: number;
  46. PriceRules?: PriceRuleGI[];
  47. availability?: object;
  48. price?: number;
  49. Locations?: LocationGI[];
  50. }
  51. export enum ServiceTypeEnum {
  52. type1,
  53. type2
  54. }
  55. export interface ServiceGI extends Sequelize.Instance<ServiceGA>, ServiceGA {
  56. getLocations: Sequelize.BelongsToManyGetAssociationsMixin<LocationGI>;
  57. setLocations: Sequelize.BelongsToManySetAssociationsMixin<LocationGI, number, 'membership_location_link'>;
  58.  
  59. getSurvey: Sequelize.BelongsToGetAssociationMixin<SurveyGI>;
  60. setSurvey: Sequelize.BelongsToSetAssociationMixin<SurveyGI, number>;
  61.  
  62. getServiceOptions: Sequelize.HasManyGetAssociationsMixin<ServiceOptionGI>;
  63. setServiceOptions: Sequelize.HasManySetAssociationsMixin<ServiceOptionGI, number>;
  64.  
  65. getServiceCategories: Sequelize.BelongsToManyGetAssociationsMixin<ServiceCategoryGI>;
  66. setServiceCategories: Sequelize.BelongsToManySetAssociationsMixin<ServiceCategoryGI, number, 'serviceLink'>;
  67.  
  68. getResourceTypes: Sequelize.BelongsToManyGetAssociationsMixin<ResourceTypeGI>;
  69. setResourceTypes: Sequelize.BelongsToManySetAssociationsMixin<ResourceTypeGI, number, ServiceResourceTypeLinkGA>;
  70.  
  71. getResources: Sequelize.BelongsToManyGetAssociationsMixin<ResourceGI>;
  72. setResources: Sequelize.BelongsToManySetAssociationsMixin<ResourceGI, number, ServiceResourceLinkGA>;
  73.  
  74. getBookingOptions: Sequelize.BelongsToManyGetAssociationsMixin<BookingOptionGI>;
  75. setBookingOptions: Sequelize.BelongsToManySetAssociationsMixin<BookingOptionGI, number, 'service_booking_option_link'>;
  76.  
  77. getNotificationTypes: Sequelize.BelongsToManyGetAssociationsMixin<NotificationTypeGI>;
  78. setNotificationType: Sequelize.BelongsToManySetAssociationsMixin<NotificationTypeGI, number, 'notification_type_service_link'>;
  79.  
  80. isIncludeResource(id: number): Promise<boolean | Error>
  81. hasSurvey(type: string): Promise<boolean>
  82. isBookingOption(bookingOptions: number[]): Promise<boolean | Error>
  83. getActualPrice(config: {customer: {id: number, membership_id? :number}, resources: ResourceGI[]}, priceRulres: PriceRuleGI[]): PriceRuleGI
  84. getAvailability(): Promise<Availability | null>
  85. loadPriceRules(): Promise<null>
  86. loadPriceConditions(config: {customer: {id: number, membership_id? :number}, resources: any[]}): Promise<{customer: {id: number, membership_id? :number}, resources: ResourceGI[]}>
  87. }
  88.  
  89. export interface ServiceCM extends Sequelize.Model<ServiceGI, ServiceGA> {}
  90.  
  91. let attributes: Sequelize.DefineAttributes = {
  92. id: {
  93. type: Sequelize.INTEGER,
  94. autoIncrement: true,
  95. primaryKey: true
  96. },
  97. name: Sequelize.STRING,
  98. type: Sequelize.ENUM('type1', 'type2'),
  99. working_hours: Sequelize.TEXT,
  100. non_available_schedule: Sequelize.TEXT,
  101. min_duration: Sequelize.INTEGER,
  102. max_duration: Sequelize.INTEGER,
  103. default_duration: Sequelize.INTEGER,
  104. time_gap: Sequelize.INTEGER,
  105. min_booking_interval: Sequelize.INTEGER,
  106. max_booking_interval: Sequelize.INTEGER,
  107. check_in_time: Sequelize.INTEGER,
  108. check_out_time: Sequelize.INTEGER,
  109. availability: Sequelize.JSON,
  110. cancel_gap: {
  111. type: Sequelize.INTEGER,
  112. defaultValue: 0
  113. },
  114. slot_duration: {
  115. type: Sequelize.INTEGER,
  116. defaultValue: 60, // sec
  117. get () {
  118. let d = this.getDataValue('slot_duration')
  119. if (!d)
  120. return 60
  121. return d
  122. }
  123. },
  124. price: {
  125. type: Sequelize.VIRTUAL,
  126. },
  127. ...localizationMixin([
  128. 'name',
  129. ]),
  130. deletedAt: {
  131. type: Sequelize.DATE,
  132. get () {
  133. return moment(this.getDataValue('deletedAt'))
  134. }
  135. },
  136. createdAt: {
  137. type: Sequelize.DATE,
  138. get () {
  139. return moment(this.getDataValue('createdAt'))
  140. }
  141. },
  142. updatedAt: {
  143. type: Sequelize.DATE,
  144. get () {
  145. return moment(this.getDataValue('createdAt'))
  146. }
  147. },
  148. }
  149.  
  150. export default (seq: Sequelize.Sequelize) => {
  151. var model = seq.define<ServiceGI, ServiceGA>("Service", attributes, {
  152. tableName: 'services',
  153. paranoid: true,
  154. })
  155.  
  156. model['prototype'].getAvailability = async function(this: ServiceGI) {
  157. let service = await model.findById(this.id, {attributes: ['availability']})
  158. return service.availability ? new Availability(<IAvailabilityConfig>service.availability) : null
  159. }
  160.  
  161. model['prototype'].loadPriceRules = async function() {
  162. await this.reload({
  163. attributes: ['name', 'min_duration', 'max_duration', 'slot_duration'],
  164. include: [
  165. {
  166. model: db.Location,
  167. attributes: ['id']
  168. },
  169. ...db.PriceRule.getPriceRuleConfig()
  170. ]
  171. })
  172. if (this.PriceRules.length == 0)
  173. throw new Error(`Service ${this.name} doesn't have price rule.`)
  174. }
  175.  
  176. model['prototype'].loadPriceConditions = async function(config: {customer: {id: number, membership_id? :number}, resources: any[]}) {
  177. if (!config.customer.membership_id || !config.customer.membership_id === undefined) {
  178. let _customerPayload = await db.Customer.findById(config.customer.id, {attributes: ['id', 'membership_id']})
  179. config.customer.membership_id = _customerPayload.membership_id
  180. }
  181. if (!config.resources['ResourceTypes']) {
  182. config.resources = await db.Resource.findAll({
  183. where: {
  184. id: {$in: config.resources}
  185. },
  186. include: [{
  187. model: db.ResourceType
  188. }]
  189. })
  190. }
  191. return config
  192. }
  193.  
  194. /**
  195. * Function should be sync!!!
  196. * @param {{customer: {id: number; membership_id?: number}; resources: any[]}} config
  197. * @return {Promise<number | Error>}
  198. */
  199. model['prototype'].getActualPrice= function (
  200. this: ServiceGI,
  201. config: {customer: {id: number, membership_id? :number}, resources: ResourceGI[]},
  202. priceRulres: PriceRuleGI[]
  203. )
  204. {
  205. // model['prototype'].loadPriceRules should be loaded before
  206.  
  207. let price;
  208. for (let rule of priceRulres) {
  209. if (rule.Memberships.length > 0) {
  210. if (!rule.Memberships.some(membership => membership.id === config.customer.membership_id)) {
  211. continue;
  212. }
  213. }
  214. if (rule.ResourceTypes.length > 0) {
  215. let st = rule.ResourceTypes.some(resType => {
  216. return config.resources.some(res => {
  217. return res.ResourceTypes.some(rt => {
  218. return rt.id === resType.id
  219. })
  220. })
  221. })
  222. if (!st) {
  223. continue
  224. }
  225. }
  226. if (rule.Resources.length > 0) {
  227. let st = rule.Resources.some(resource => {
  228. return config.resources.some(r => {
  229. return r.id === resource.id
  230. })
  231. })
  232. if (!st) {
  233. continue
  234. }
  235. }
  236. if (rule.Locations.length > 0) {
  237. let st = rule.Locations.some(location => {
  238. return this.Locations.some(loc => {
  239. return location.id === loc.id
  240. })
  241. })
  242. if (!st) {
  243. continue
  244. }
  245. }
  246. if (rule.Customers.length > 0) {
  247. if (!rule.Customers.some(cm => cm.id === config.customer.id)) {
  248. continue
  249. }
  250. }
  251.  
  252. if (!price) {
  253. price = rule
  254. } else {
  255. if (rule.priority > price.priority) {
  256. price = rule
  257. }
  258. }
  259. }
  260. if (!price) {
  261. throw new PriceNotFound(`Price rules are not matched`)
  262. }
  263. return price
  264. }
  265.  
  266. model['prototype'].hasSurvey = async function(this: ServiceGI, type: string) {
  267. let s = {}
  268. switch (type) {
  269. case 'checked_in':
  270. s['survey_check_in_id'] = {$ne: null};
  271. break;
  272. case 'checked_out':
  273. s['survey_check_out_id'] = {$ne: null};
  274. break;
  275. }
  276. let count = await model.count({
  277. where: {
  278. id: this.id,
  279. ...s
  280. }
  281. })
  282. return count > 0
  283. }
  284.  
  285. model['prototype'].isIncludeResource = async function(id: number) {
  286. let c = await model.count({
  287. where: {
  288. id: this.id
  289. },
  290. include: [
  291. {
  292. model: db.Resource,
  293. required: true,
  294. where: {
  295. id
  296. }
  297. }
  298. ]
  299. })
  300. if (!c) {
  301. throw new Error(`Resource doesn't associate to service`)
  302. }
  303. }
  304.  
  305. model['prototype'].isBookingOption = async function (this: ServiceGI, bookingOptions: number[]) {
  306. if (bookingOptions.length === 0 ) return true
  307. let count = await db.BookingOption.count({
  308. where: {
  309. id: {$in: bookingOptions}
  310. },
  311. include: [
  312. {
  313. model: db.Service,
  314. where: {
  315. id: this.id
  316. }
  317. }
  318. ]})
  319. if (count !== bookingOptions.length) {
  320. throw new Error(`Some of booking options doesn't included to service`);
  321. }
  322. return true
  323. }
  324.  
  325. model['i18nUpdate'] = i18nUpdate
  326. model['prototype'].getI18nTitle = getI18nTitle;
  327.  
  328. model['associate'] = function (db: SequelizeModels) {
  329. let {
  330. Location, Survey, ServiceOption, ServiceCategory,
  331. ServiceResourceTypeLink, ResourceType, ServiceResourceLink,
  332. Resource, NotificationType, Membership, BookingOption
  333. } = db
  334. model.belongsToMany(Location, {through: 'service_location_link', foreignKey: 'service_id'});
  335. model.hasMany(ServiceOption, {foreignKey: 'service_id', as: 'service_options'})
  336. model.belongsTo(Survey, {foreignKey: 'survey_check_in_id', as: 'survey_check_in'})
  337. model.belongsTo(Survey, {foreignKey: 'survey_check_out_id', as: 'survey_check_out'})
  338. model.belongsToMany(ServiceCategory, {through: 'service_link', foreignKey: 'service_category_id'});
  339. model.belongsToMany(ResourceType, {through: {model: ServiceResourceTypeLink, unique: false}, foreignKey: 'service_id'});
  340. model.belongsToMany(Resource, {through: {model: ServiceResourceLink, unique: false}, foreignKey: 'service_id'});
  341. model.belongsToMany(NotificationType, {through: 'notification_type_service_link', foreignKey: 'service_id'});
  342. model.belongsToMany(Membership, {through: 'membership_service_link', foreignKey: 'service_id'})
  343. model.belongsToMany(BookingOption, {through: 'service_booking_option_link', foreignKey: 'service_id'})
  344. model.belongsToMany(db.PriceRule, {through: 'service_price_rule_link', foreignKey: 'service_id'})
  345. }
  346.  
  347. return model;
  348. }
Add Comment
Please, Sign In to add comment