static async create(data, uniqueChecker) {
        let isNew = false;
        let id;

        if (data.id instanceof ObjectID) {
            id = data.id;
        } else if (typeof data.id === 'string') {
            id = new ObjectID(data.id);
        } else {
            id = new ObjectID();
            isNew = true;
        }

        const name = OfferName.create(data.name);
        const code = OfferCode.create(data.code);
        const title = OfferTitle.create(data.display_title);
        const description = OfferDescription.create(data.display_description);
        const type = OfferType.create(data.type);
        const cadence = OfferCadence.create(data.cadence);
        const duration = OfferDuration.create(data.duration, data.duration_in_months);
        const status = OfferStatus.create(data.status || 'active');
        const createdAt = isNew ? OfferCreatedAt.create() : OfferCreatedAt.create(data.created_at);
        const lastRedeemed = data.last_redeemed ? new Date(data.last_redeemed).toISOString() : null;

        if (isNew && data.redemptionCount !== undefined) {
            // TODO correct error
            throw new errors.InvalidOfferCode({
                message: 'An Offer cannot be created with redemptionCount'
            });
        }

        const redemptionCount = data.redemptionCount || 0;

        if (cadence.value === 'year' && duration.value.type === 'repeating') {
            throw new errors.InvalidOfferDuration({
                message: 'Offer `duration` must be "once" or "forever" for the "yearly" cadence.'
            });
        }

        //CASE: For offer type trial, the duration can only be `trial`
        if (type.value === 'trial' && duration.value.type !== 'trial') {
            throw new errors.InvalidOfferDuration({
                message: 'Offer `duration` must be "trial" for offer type "trial".'
            });
        }

        let currency = null;
        let amount;
        if (type.equals(OfferType.Percentage)) {
            amount = OfferAmount.OfferPercentageAmount.create(data.amount);
        } else if (type.equals(OfferType.Trial)) {
            amount = OfferAmount.OfferTrialAmount.create(data.amount);
        } else if (type.equals(OfferType.Fixed)) {
            amount = OfferAmount.OfferFixedAmount.create(data.amount);
            currency = OfferCurrency.create(data.currency);
        }

        if (isNew) {
            if (!await uniqueChecker.isUniqueName(name)) {
                throw new errors.InvalidOfferName({
                    message: `Offer 'name' must be unique. Please change and try again.`
                });
            }
        }

        if (isNew) {
            if (!await uniqueChecker.isUniqueCode(code)) {
                throw new errors.InvalidOfferCode({
                    message: `Offer 'code' must be unique. Please change and try again.`
                });
            }
        }

        const tier = OfferTier.create(data.tier);

        return new Offer({
            id,
            name,
            code,
            display_title: title,
            display_description: description,
            type,
            amount,
            cadence,
            duration,
            currency,
            tier,
            redemptionCount,
            status,
            createdAt,
            lastRedeemed
        }, {isNew});
    }
@param {OfferCreateProps} data
@param {UniqueChecker} uniqueChecker
@returns {Promise<Offer>}