adapty 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/commands/asa/ad-groups/create.d.ts +1 -0
- package/dist/commands/asa/ad-groups/create.js +19 -4
- package/dist/commands/asa/campaigns/create.d.ts +5 -0
- package/dist/commands/asa/campaigns/create.js +15 -3
- package/dist/commands/asa/campaigns/update.d.ts +5 -0
- package/dist/commands/asa/campaigns/update.js +14 -3
- package/dist/lib/asa-flags.d.ts +22 -1
- package/dist/lib/asa-flags.js +47 -0
- package/dist/lib/asa-schemas.d.ts +19 -0
- package/oclif.manifest.json +481 -399
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -137,11 +137,15 @@ Campaign structure. These lists return metadata only — numbers come from `asa
|
|
|
137
137
|
adapty asa campaigns list
|
|
138
138
|
adapty asa campaigns get CAMPAIGN_ID
|
|
139
139
|
adapty asa campaigns create --org UUID --name "Winter push" --adam-id 123456 --country US --daily-budget 50
|
|
140
|
+
adapty asa campaigns create --org UUID --name "LOC push" --adam-id 123456 --country US --daily-budget 50 \
|
|
141
|
+
--invoice-advertiser "Acme Inc" --invoice-order-number PO-42 --invoice-contact-name "Jane Doe" \
|
|
142
|
+
--invoice-contact-email jane@acme.com --invoice-billing-email billing@acme.com # payment_model LOC
|
|
140
143
|
adapty asa campaigns update CAMPAIGN_ID [--status PAUSED] [--daily-budget 80] [--country US]
|
|
141
144
|
|
|
142
145
|
adapty asa ad-groups list
|
|
143
146
|
adapty asa ad-groups get AD_GROUP_ID
|
|
144
147
|
adapty asa ad-groups create --campaign UUID --name "Brand terms" --default-bid 1.20
|
|
148
|
+
adapty asa ad-groups create --campaign UUID --name "Automated Max Conv" --automated # Max Conversions campaigns
|
|
145
149
|
adapty asa ad-groups update AD_GROUP_ID [--default-bid 1.50] [--status PAUSED]
|
|
146
150
|
|
|
147
151
|
adapty asa ads list
|
|
@@ -5,6 +5,7 @@ export default class AsaAdGroupsCreate extends Command {
|
|
|
5
5
|
static enableJsonFlag: boolean;
|
|
6
6
|
static examples: string[];
|
|
7
7
|
static flags: {
|
|
8
|
+
automated: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
9
|
'automated-keywords': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
10
|
campaign: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
11
|
'cpa-goal': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -5,11 +5,12 @@ import { currencyFlag, idempotencyFlags, money, moneyFlag, pricingModelFlag, sch
|
|
|
5
5
|
import { isValidUuid } from '../../../lib/flags.js';
|
|
6
6
|
import { printResponse } from '../../../lib/output.js';
|
|
7
7
|
export default class AsaAdGroupsCreate extends Command {
|
|
8
|
-
static description = 'Create an ad group inside a campaign';
|
|
8
|
+
static description = 'Create an ad group inside a campaign; --automated creates the automated ad group a Max Conversions campaign needs to run';
|
|
9
9
|
static enableJsonFlag = true;
|
|
10
10
|
static examples = [
|
|
11
11
|
'<%= config.bin %> asa ad-groups create --campaign UUID --name "Brand terms" --default-bid 1.20',
|
|
12
12
|
'<%= config.bin %> asa ad-groups create --campaign UUID --name "Brand terms" --default-bid 1.20 --start-time 2026-09-01 --pricing-model CPM',
|
|
13
|
+
'<%= config.bin %> asa ad-groups create --campaign UUID --name "Automated Max Conv" --automated',
|
|
13
14
|
];
|
|
14
15
|
static flags = {
|
|
15
16
|
...currencyFlag,
|
|
@@ -17,10 +18,14 @@ export default class AsaAdGroupsCreate extends Command {
|
|
|
17
18
|
...pricingModelFlag,
|
|
18
19
|
...confirmFlags,
|
|
19
20
|
...idempotencyFlags,
|
|
21
|
+
automated: Flags.boolean({
|
|
22
|
+
description: 'Create the automated ad group a Max Conversions campaign needs (no bid, no schedule, always ENABLED)',
|
|
23
|
+
exclusive: ['automated-keywords'],
|
|
24
|
+
}),
|
|
20
25
|
'automated-keywords': Flags.boolean({ allowNo: true, description: 'Let Apple add keywords automatically' }),
|
|
21
26
|
campaign: Flags.string({ description: 'Campaign ID (UUID)', required: true }),
|
|
22
27
|
'cpa-goal': moneyFlag('CPA goal'),
|
|
23
|
-
'default-bid': moneyFlag('Default bid
|
|
28
|
+
'default-bid': moneyFlag('Default bid; required unless --automated is set'),
|
|
24
29
|
name: Flags.string({ description: 'Ad group name', required: true }),
|
|
25
30
|
status: Flags.string({ description: 'Initial status', options: ['ENABLED', 'PAUSED'] }),
|
|
26
31
|
};
|
|
@@ -28,15 +33,25 @@ export default class AsaAdGroupsCreate extends Command {
|
|
|
28
33
|
const { flags } = await this.parse(AsaAdGroupsCreate);
|
|
29
34
|
if (!isValidUuid(flags.campaign))
|
|
30
35
|
this.error('Invalid campaign ID format.', { exit: 2 });
|
|
36
|
+
if (!flags.automated && flags['default-bid'] === undefined) {
|
|
37
|
+
this.error('--default-bid is required unless --automated is set.', { exit: 2 });
|
|
38
|
+
}
|
|
39
|
+
if (flags.automated && flags['start-time'] !== undefined) {
|
|
40
|
+
this.error('--start-time is not allowed with --automated: Apple schedules the automated ad group itself.', { exit: 2 });
|
|
41
|
+
}
|
|
42
|
+
if (flags.automated && flags.status === 'PAUSED') {
|
|
43
|
+
this.error('An automated ad group must be ENABLED; pause the campaign instead.', { exit: 2 });
|
|
44
|
+
}
|
|
31
45
|
const body = {
|
|
32
|
-
automated_keywords_opt_in: flags['automated-keywords'],
|
|
46
|
+
automated_keywords_opt_in: flags.automated ? true : flags['automated-keywords'],
|
|
47
|
+
automated_keywords_required: flags.automated ? true : undefined,
|
|
33
48
|
campaign_id: flags.campaign,
|
|
34
49
|
cpa_goal: money(flags['cpa-goal'], flags.currency),
|
|
35
50
|
default_bid_amount: money(flags['default-bid'], flags.currency),
|
|
36
51
|
end_time: startOfDayUtc(flags['end-time']),
|
|
37
52
|
name: flags.name,
|
|
38
53
|
pricing_model: flags['pricing-model'],
|
|
39
|
-
start_time: startOfDayUtc(flags['start-time'] ?? todayUtc()),
|
|
54
|
+
start_time: flags.automated ? undefined : startOfDayUtc(flags['start-time'] ?? todayUtc()),
|
|
40
55
|
status: flags.status,
|
|
41
56
|
};
|
|
42
57
|
await confirmMutation(this, { body, method: 'POST', path: '/ad-groups/', summary: `Create ad group ${flags.name}` }, flags.yes);
|
|
@@ -17,6 +17,11 @@ export default class AsaCampaignsCreate extends Command {
|
|
|
17
17
|
status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
18
|
'supply-source': import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
19
19
|
'target-cpa': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
|
+
'invoice-advertiser': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
'invoice-billing-email': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
22
|
+
'invoice-contact-email': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
23
|
+
'invoice-contact-name': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
24
|
+
'invoice-order-number': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
25
|
'idempotency-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
26
|
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
22
27
|
currency: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { Command, Flags } from '@oclif/core';
|
|
2
2
|
import { asaWrite, createAsaClient, noteReplay } from '../../../lib/asa-client.js';
|
|
3
3
|
import { confirmFlags, confirmMutation } from '../../../lib/asa-confirm.js';
|
|
4
|
-
import { currencyFlag, idempotencyFlags, money, moneyFlag } from '../../../lib/asa-flags.js';
|
|
4
|
+
import { currencyFlag, idempotencyFlags, invoiceFlags, locInvoiceDetails, money, moneyFlag, reportServingState, } from '../../../lib/asa-flags.js';
|
|
5
5
|
import { isValidUuid } from '../../../lib/flags.js';
|
|
6
6
|
import { printResponse } from '../../../lib/output.js';
|
|
7
7
|
export default class AsaCampaignsCreate extends Command {
|
|
8
|
-
static description = 'Create a campaign in Apple Search Ads';
|
|
8
|
+
static description = 'Create a campaign in Apple Search Ads. A Max Conversions campaign also needs an automated ad group (`asa ad-groups create --automated`); line-of-credit organizations must pass all five --invoice-* flags';
|
|
9
9
|
static enableJsonFlag = true;
|
|
10
10
|
static examples = [
|
|
11
11
|
'<%= config.bin %> asa campaigns create --org UUID --name "Winter push" --adam-id 123456 --country US --daily-budget 50',
|
|
12
|
+
'<%= config.bin %> asa campaigns create --org UUID --name "Max Conv" --adam-id 123456 --country US --daily-budget 50 --bidding-strategy MAX_CONVERSIONS',
|
|
13
|
+
'<%= config.bin %> asa campaigns create --org UUID --name "LOC push" --adam-id 123456 --country US --daily-budget 50 --invoice-advertiser "Acme Inc" --invoice-order-number PO-42 --invoice-contact-name "Jane Doe" --invoice-contact-email jane@acme.com --invoice-billing-email billing@acme.com',
|
|
12
14
|
];
|
|
13
15
|
static flags = {
|
|
14
16
|
...currencyFlag,
|
|
15
17
|
...confirmFlags,
|
|
16
18
|
...idempotencyFlags,
|
|
19
|
+
...invoiceFlags,
|
|
17
20
|
'ad-channel-type': Flags.string({ default: 'SEARCH', description: 'Ad channel type', options: ['DISPLAY', 'SEARCH'] }),
|
|
18
21
|
'adam-id': Flags.integer({ description: 'App Store app ID (adam_id)', required: true }),
|
|
19
22
|
'bidding-strategy': Flags.string({
|
|
@@ -32,12 +35,19 @@ export default class AsaCampaignsCreate extends Command {
|
|
|
32
35
|
description: 'Supply source, repeatable',
|
|
33
36
|
multiple: true,
|
|
34
37
|
}),
|
|
35
|
-
'target-cpa': moneyFlag('Target CPA'),
|
|
38
|
+
'target-cpa': moneyFlag('Target CPA; must be lower than --daily-budget'),
|
|
36
39
|
};
|
|
37
40
|
async run() {
|
|
38
41
|
const { flags } = await this.parse(AsaCampaignsCreate);
|
|
39
42
|
if (!isValidUuid(flags.org))
|
|
40
43
|
this.error('Invalid org ID format.', { exit: 2 });
|
|
44
|
+
let invoice;
|
|
45
|
+
try {
|
|
46
|
+
invoice = locInvoiceDetails(flags);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
this.error(error.message, { exit: 2 });
|
|
50
|
+
}
|
|
41
51
|
const body = {
|
|
42
52
|
ad_channel_type: flags['ad-channel-type'],
|
|
43
53
|
adam_id: flags['adam-id'],
|
|
@@ -47,6 +57,7 @@ export default class AsaCampaignsCreate extends Command {
|
|
|
47
57
|
campaign_group_id: flags.org,
|
|
48
58
|
countries_or_regions: flags.country,
|
|
49
59
|
daily_budget_amount: money(flags['daily-budget'], flags.currency),
|
|
60
|
+
loc_invoice_details: invoice,
|
|
50
61
|
name: flags.name,
|
|
51
62
|
status: flags.status,
|
|
52
63
|
supply_sources: flags['supply-source'],
|
|
@@ -62,6 +73,7 @@ export default class AsaCampaignsCreate extends Command {
|
|
|
62
73
|
if (result.campaign && !replayed)
|
|
63
74
|
this.log('Campaign created!');
|
|
64
75
|
printResponse(result, this.log.bind(this));
|
|
76
|
+
reportServingState(result.campaign, this.warn.bind(this));
|
|
65
77
|
return result;
|
|
66
78
|
}
|
|
67
79
|
}
|
|
@@ -15,6 +15,11 @@ export default class AsaCampaignsUpdate extends Command {
|
|
|
15
15
|
name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
16
|
status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
17
|
'target-cpa': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
'invoice-advertiser': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
'invoice-billing-email': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
|
+
'invoice-contact-email': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
'invoice-contact-name': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
22
|
+
'invoice-order-number': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
23
|
'idempotency-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
24
|
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
20
25
|
currency: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
2
|
import { asaWrite, createAsaClient, noteReplay } from '../../../lib/asa-client.js';
|
|
3
3
|
import { confirmFlags, confirmMutation } from '../../../lib/asa-confirm.js';
|
|
4
|
-
import { currencyFlag, idempotencyFlags, money, moneyFlag } from '../../../lib/asa-flags.js';
|
|
4
|
+
import { currencyFlag, idempotencyFlags, invoiceFlags, locInvoiceDetails, money, moneyFlag, reportServingState, } from '../../../lib/asa-flags.js';
|
|
5
5
|
import { isValidUuid } from '../../../lib/flags.js';
|
|
6
6
|
import { printResponse } from '../../../lib/output.js';
|
|
7
7
|
export default class AsaCampaignsUpdate extends Command {
|
|
8
8
|
static args = {
|
|
9
9
|
campaign_id: Args.string({ description: 'Campaign ID (UUID)', required: true }),
|
|
10
10
|
};
|
|
11
|
-
static description = 'Change a campaign: budgets, countries, status or
|
|
11
|
+
static description = 'Change a campaign: budgets, countries, status, schedule or Invoicing Options (all five --invoice-* flags together; they replace the stored set)';
|
|
12
12
|
static enableJsonFlag = true;
|
|
13
13
|
static examples = [
|
|
14
14
|
'<%= config.bin %> asa campaigns update UUID --status PAUSED',
|
|
15
15
|
'<%= config.bin %> asa campaigns update UUID --daily-budget 80',
|
|
16
|
+
'<%= config.bin %> asa campaigns update UUID --invoice-advertiser "Acme Inc" --invoice-order-number PO-42 --invoice-contact-name "Jane Doe" --invoice-contact-email jane@acme.com --invoice-billing-email billing@acme.com',
|
|
16
17
|
];
|
|
17
18
|
static flags = {
|
|
18
19
|
...currencyFlag,
|
|
19
20
|
...confirmFlags,
|
|
20
21
|
...idempotencyFlags,
|
|
22
|
+
...invoiceFlags,
|
|
21
23
|
'bidding-strategy': Flags.string({
|
|
22
24
|
description: 'Bidding strategy',
|
|
23
25
|
options: ['MANUAL_CPT', 'MAX_CONVERSIONS'],
|
|
@@ -48,8 +50,16 @@ export default class AsaCampaignsUpdate extends Command {
|
|
|
48
50
|
body.target_cpa = money(flags['target-cpa'], flags.currency);
|
|
49
51
|
if (flags['bidding-strategy'] !== undefined)
|
|
50
52
|
body.bidding_strategy = flags['bidding-strategy'];
|
|
53
|
+
try {
|
|
54
|
+
const details = locInvoiceDetails(flags);
|
|
55
|
+
if (details)
|
|
56
|
+
body.loc_invoice_details = details;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
this.error(error.message, { exit: 2 });
|
|
60
|
+
}
|
|
51
61
|
if (Object.keys(body).length === 0) {
|
|
52
|
-
this.error('Nothing to change. Pass at least one field, e.g. --status PAUSED.', { exit: 2 });
|
|
62
|
+
this.error('Nothing to change. Pass at least one field, e.g. --status PAUSED, or the five --invoice-* flags for a line-of-credit organization.', { exit: 2 });
|
|
53
63
|
}
|
|
54
64
|
await confirmMutation(this, { body, method: 'PUT', path: `/campaigns/${args.campaign_id}/`, summary: 'Update campaign' }, flags.yes);
|
|
55
65
|
const client = await createAsaClient(this.config);
|
|
@@ -61,6 +71,7 @@ export default class AsaCampaignsUpdate extends Command {
|
|
|
61
71
|
if (result.campaign && !replayed)
|
|
62
72
|
this.log('Campaign updated!');
|
|
63
73
|
printResponse(result, this.log.bind(this));
|
|
74
|
+
reportServingState(result.campaign, this.warn.bind(this));
|
|
64
75
|
return result;
|
|
65
76
|
}
|
|
66
77
|
}
|
package/dist/lib/asa-flags.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { QueryParams } from './api-client.js';
|
|
2
|
-
import type { AsaMoney, AsaMutationError } from './asa-schemas.js';
|
|
2
|
+
import type { AsaLocInvoiceDetails, AsaMoney, AsaMutationError } from './asa-schemas.js';
|
|
3
3
|
export declare const MAX_BULK_ITEMS = 100;
|
|
4
4
|
export declare const MAX_BY_DAYS = 16;
|
|
5
5
|
export declare const ASA_METRIC_ENTITIES: string[];
|
|
@@ -79,6 +79,27 @@ export declare const idempotencyFlags: {
|
|
|
79
79
|
'idempotency-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
80
80
|
};
|
|
81
81
|
export declare function money(amount: string | undefined, currency: string): AsaMoney | undefined;
|
|
82
|
+
declare const INVOICE_FIELDS: {
|
|
83
|
+
readonly 'invoice-advertiser': "client_name";
|
|
84
|
+
readonly 'invoice-billing-email': "billing_contact_email";
|
|
85
|
+
readonly 'invoice-contact-email': "buyer_email";
|
|
86
|
+
readonly 'invoice-contact-name': "buyer_name";
|
|
87
|
+
readonly 'invoice-order-number': "order_number";
|
|
88
|
+
};
|
|
89
|
+
type InvoiceFlagName = keyof typeof INVOICE_FIELDS;
|
|
90
|
+
export declare const invoiceFlags: {
|
|
91
|
+
'invoice-advertiser': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
92
|
+
'invoice-billing-email': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
93
|
+
'invoice-contact-email': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
94
|
+
'invoice-contact-name': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
95
|
+
'invoice-order-number': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
96
|
+
};
|
|
97
|
+
export declare function locInvoiceDetails(flags: Partial<Record<InvoiceFlagName, string>>): Record<keyof AsaLocInvoiceDetails, string> | undefined;
|
|
98
|
+
export declare function reportServingState(campaign: null | {
|
|
99
|
+
internal_id: string;
|
|
100
|
+
serving_state_reasons?: null | string[];
|
|
101
|
+
serving_status?: null | string;
|
|
102
|
+
}, warn: (msg: string) => void): void;
|
|
82
103
|
interface BulkOutcome {
|
|
83
104
|
applied: unknown[];
|
|
84
105
|
errors: AsaMutationError[];
|
package/dist/lib/asa-flags.js
CHANGED
|
@@ -124,6 +124,53 @@ export const idempotencyFlags = {
|
|
|
124
124
|
export function money(amount, currency) {
|
|
125
125
|
return amount === undefined ? undefined : { amount, currency };
|
|
126
126
|
}
|
|
127
|
+
const INVOICE_FIELDS = {
|
|
128
|
+
'invoice-advertiser': 'client_name',
|
|
129
|
+
'invoice-billing-email': 'billing_contact_email',
|
|
130
|
+
'invoice-contact-email': 'buyer_email',
|
|
131
|
+
'invoice-contact-name': 'buyer_name',
|
|
132
|
+
'invoice-order-number': 'order_number',
|
|
133
|
+
};
|
|
134
|
+
const INVOICE_PREFIX = 'Invoicing Options (line-of-credit orgs, see payment_model in asa orgs list)';
|
|
135
|
+
function invoiceFlag(what) {
|
|
136
|
+
return Flags.string({ description: `${INVOICE_PREFIX}: ${what}` });
|
|
137
|
+
}
|
|
138
|
+
export const invoiceFlags = {
|
|
139
|
+
'invoice-advertiser': invoiceFlag('advertiser name'),
|
|
140
|
+
'invoice-billing-email': invoiceFlag('billing contact email'),
|
|
141
|
+
'invoice-contact-email': invoiceFlag('buyer contact email'),
|
|
142
|
+
'invoice-contact-name': invoiceFlag('buyer contact name'),
|
|
143
|
+
'invoice-order-number': invoiceFlag('order number'),
|
|
144
|
+
};
|
|
145
|
+
export function locInvoiceDetails(flags) {
|
|
146
|
+
const names = Object.keys(INVOICE_FIELDS);
|
|
147
|
+
const missing = names.filter((name) => flags[name] === undefined);
|
|
148
|
+
if (missing.length === names.length)
|
|
149
|
+
return undefined;
|
|
150
|
+
if (missing.length > 0) {
|
|
151
|
+
throw new Error(`Invoicing Options need all five flags; missing: ${missing.map((name) => `--${name}`).join(', ')}`);
|
|
152
|
+
}
|
|
153
|
+
return Object.fromEntries(names.map((name) => [INVOICE_FIELDS[name], flags[name]]));
|
|
154
|
+
}
|
|
155
|
+
const SERVING_HINTS = {
|
|
156
|
+
AUTOMATED_KEYWORDS_REQUIRED_AD_GROUP_MISSING: (campaignId) => `Max Conversions campaign needs an automated ad group: adapty asa ad-groups create --campaign ${campaignId} --name "Automated Max Conv" --automated`,
|
|
157
|
+
MISSING_BO_OR_INVOICING_FIELDS: (campaignId) => `This organization bills by line of credit — add Invoicing Options: adapty asa campaigns update ${campaignId} --invoice-advertiser ... --invoice-order-number ... --invoice-contact-name ... --invoice-contact-email ... --invoice-billing-email ...`,
|
|
158
|
+
};
|
|
159
|
+
export function reportServingState(campaign, warn) {
|
|
160
|
+
if (campaign?.serving_status !== 'NOT_RUNNING' || !campaign.serving_state_reasons?.length)
|
|
161
|
+
return;
|
|
162
|
+
const reasons = campaign.serving_state_reasons;
|
|
163
|
+
if (reasons.length === 1 && reasons[0] === 'PAUSED_BY_USER')
|
|
164
|
+
return;
|
|
165
|
+
for (const reason of reasons) {
|
|
166
|
+
const hint = SERVING_HINTS[reason];
|
|
167
|
+
if (hint)
|
|
168
|
+
warn(hint(campaign.internal_id));
|
|
169
|
+
}
|
|
170
|
+
const other = reasons.filter((reason) => !SERVING_HINTS[reason]);
|
|
171
|
+
if (other.length > 0)
|
|
172
|
+
warn(`Not serving: ${other.join(', ')}`);
|
|
173
|
+
}
|
|
127
174
|
export function reportBulkOutcome({ applied, errors, isValidationFailure, kind }, log) {
|
|
128
175
|
if (isValidationFailure) {
|
|
129
176
|
log(`Nothing was applied: the batch failed validation before Apple was called.`);
|
|
@@ -48,6 +48,13 @@ export interface AsaAppDTO {
|
|
|
48
48
|
last_synced_at: string;
|
|
49
49
|
name: string;
|
|
50
50
|
}
|
|
51
|
+
export interface AsaLocInvoiceDetails {
|
|
52
|
+
billing_contact_email: null | string;
|
|
53
|
+
buyer_email: null | string;
|
|
54
|
+
buyer_name: null | string;
|
|
55
|
+
client_name: null | string;
|
|
56
|
+
order_number: null | string;
|
|
57
|
+
}
|
|
51
58
|
export interface AsaCampaignGroupDTO {
|
|
52
59
|
currency: string;
|
|
53
60
|
internal_id: string;
|
|
@@ -55,6 +62,7 @@ export interface AsaCampaignGroupDTO {
|
|
|
55
62
|
org_id: number;
|
|
56
63
|
org_name: string;
|
|
57
64
|
parent_org_id: number;
|
|
65
|
+
payment_model: null | string;
|
|
58
66
|
time_zone: string;
|
|
59
67
|
}
|
|
60
68
|
export interface AsaCampaignDTO {
|
|
@@ -70,8 +78,12 @@ export interface AsaCampaignDTO {
|
|
|
70
78
|
daily_budget_amount: AsaMoney | null;
|
|
71
79
|
end_time: null | string;
|
|
72
80
|
internal_id: string;
|
|
81
|
+
loc_invoice_details: AsaLocInvoiceDetails | null;
|
|
73
82
|
name: string;
|
|
74
83
|
org_id: number;
|
|
84
|
+
payment_model: null | string;
|
|
85
|
+
serving_state_reasons: null | string[];
|
|
86
|
+
serving_status: null | string;
|
|
75
87
|
start_time: null | string;
|
|
76
88
|
status: AsaStatus;
|
|
77
89
|
supply_sources: string[];
|
|
@@ -81,6 +93,7 @@ export interface AsaAdGroupDTO {
|
|
|
81
93
|
ad_group_id: number;
|
|
82
94
|
app_id: string;
|
|
83
95
|
automated_keywords_opt_in: boolean | null;
|
|
96
|
+
automated_keywords_required: boolean | null;
|
|
84
97
|
bidding_strategy: null | string;
|
|
85
98
|
campaign_group_id: string;
|
|
86
99
|
campaign_id: string;
|
|
@@ -91,6 +104,8 @@ export interface AsaAdGroupDTO {
|
|
|
91
104
|
name: string;
|
|
92
105
|
payment_model: null | string;
|
|
93
106
|
pricing_model: null | string;
|
|
107
|
+
serving_state_reasons: null | string[];
|
|
108
|
+
serving_status: null | string;
|
|
94
109
|
start_time: null | string;
|
|
95
110
|
status: AsaStatus | null;
|
|
96
111
|
target_cpa: AsaMoney | null;
|
|
@@ -261,7 +276,11 @@ export interface AsaCampaignMutationEntity {
|
|
|
261
276
|
campaign_group_id: string;
|
|
262
277
|
campaign_id: number;
|
|
263
278
|
internal_id: string;
|
|
279
|
+
loc_invoice_details: AsaLocInvoiceDetails | null;
|
|
264
280
|
name: string;
|
|
281
|
+
payment_model: null | string;
|
|
282
|
+
serving_state_reasons: null | string[];
|
|
283
|
+
serving_status: null | string;
|
|
265
284
|
status: AsaStatus;
|
|
266
285
|
}
|
|
267
286
|
export interface AsaAdGroupMutationDTO {
|