@things-factory/sales 0.0.0-stage → 10.1.176
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/CHANGELOG.md +8 -0
- package/assets/images/hatiolab-logo.png +0 -0
- package/dist-client/bootstrap.d.ts +1 -0
- package/dist-client/bootstrap.js +5 -0
- package/dist-client/bootstrap.js.map +1 -0
- package/dist-client/entity360/opportunity-360.d.ts +12 -0
- package/dist-client/entity360/opportunity-360.js +114 -0
- package/dist-client/entity360/opportunity-360.js.map +1 -0
- package/dist-client/index.d.ts +4 -0
- package/dist-client/index.js +5 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/opportunity-attach.d.ts +17 -0
- package/dist-client/opportunity-attach.js +8 -0
- package/dist-client/opportunity-attach.js.map +1 -0
- package/dist-client/opportunity-words.d.ts +1 -0
- package/dist-client/opportunity-words.js +13 -0
- package/dist-client/opportunity-words.js.map +1 -0
- package/dist-client/pages/opportunity-editor.d.ts +81 -0
- package/dist-client/pages/opportunity-editor.js +482 -0
- package/dist-client/pages/opportunity-editor.js.map +1 -0
- package/dist-client/pages/opportunity-list.d.ts +40 -0
- package/dist-client/pages/opportunity-list.js +134 -0
- package/dist-client/pages/opportunity-list.js.map +1 -0
- package/dist-client/pages/quotation-editor.d.ts +44 -0
- package/dist-client/pages/quotation-editor.js +315 -0
- package/dist-client/pages/quotation-editor.js.map +1 -0
- package/dist-client/pages/quotation-list.d.ts +36 -0
- package/dist-client/pages/quotation-list.js +113 -0
- package/dist-client/pages/quotation-list.js.map +1 -0
- package/dist-client/quotation-attach.d.ts +16 -0
- package/dist-client/quotation-attach.js +8 -0
- package/dist-client/quotation-attach.js.map +1 -0
- package/dist-client/quotation-words.d.ts +1 -0
- package/dist-client/quotation-words.js +13 -0
- package/dist-client/quotation-words.js.map +1 -0
- package/dist-client/route.d.ts +1 -0
- package/dist-client/route.js +17 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-server/controllers/index.d.ts +0 -0
- package/dist-server/controllers/index.js +1 -0
- package/dist-server/controllers/index.js.map +1 -0
- package/dist-server/index.d.ts +4 -0
- package/dist-server/index.js +8 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/middlewares/index.d.ts +2 -0
- package/dist-server/middlewares/index.js +7 -0
- package/dist-server/middlewares/index.js.map +1 -0
- package/dist-server/migrations/index.d.ts +1 -0
- package/dist-server/migrations/index.js +12 -0
- package/dist-server/migrations/index.js.map +1 -0
- package/dist-server/routes.d.ts +0 -0
- package/dist-server/routes.js +24 -0
- package/dist-server/routes.js.map +1 -0
- package/dist-server/service/index.d.ts +16 -0
- package/dist-server/service/index.js +21 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/opportunity/index.d.ts +8 -0
- package/dist-server/service/opportunity/index.js +12 -0
- package/dist-server/service/opportunity/index.js.map +1 -0
- package/dist-server/service/opportunity/opportunity-event-query.d.ts +9 -0
- package/dist-server/service/opportunity/opportunity-event-query.js +55 -0
- package/dist-server/service/opportunity/opportunity-event-query.js.map +1 -0
- package/dist-server/service/opportunity/opportunity-event.d.ts +27 -0
- package/dist-server/service/opportunity/opportunity-event.js +115 -0
- package/dist-server/service/opportunity/opportunity-event.js.map +1 -0
- package/dist-server/service/opportunity/opportunity-mutation.d.ts +7 -0
- package/dist-server/service/opportunity/opportunity-mutation.js +64 -0
- package/dist-server/service/opportunity/opportunity-mutation.js.map +1 -0
- package/dist-server/service/opportunity/opportunity-query.d.ts +19 -0
- package/dist-server/service/opportunity/opportunity-query.js +144 -0
- package/dist-server/service/opportunity/opportunity-query.js.map +1 -0
- package/dist-server/service/opportunity/opportunity-rules.d.ts +22 -0
- package/dist-server/service/opportunity/opportunity-rules.js +59 -0
- package/dist-server/service/opportunity/opportunity-rules.js.map +1 -0
- package/dist-server/service/opportunity/opportunity-service.d.ts +19 -0
- package/dist-server/service/opportunity/opportunity-service.js +107 -0
- package/dist-server/service/opportunity/opportunity-service.js.map +1 -0
- package/dist-server/service/opportunity/opportunity-type.d.ts +23 -0
- package/dist-server/service/opportunity/opportunity-type.js +86 -0
- package/dist-server/service/opportunity/opportunity-type.js.map +1 -0
- package/dist-server/service/opportunity/opportunity.d.ts +35 -0
- package/dist-server/service/opportunity/opportunity.js +147 -0
- package/dist-server/service/opportunity/opportunity.js.map +1 -0
- package/dist-server/service/quotation/index.d.ts +10 -0
- package/dist-server/service/quotation/index.js +14 -0
- package/dist-server/service/quotation/index.js.map +1 -0
- package/dist-server/service/quotation/quotation-event-query.d.ts +9 -0
- package/dist-server/service/quotation/quotation-event-query.js +55 -0
- package/dist-server/service/quotation/quotation-event-query.js.map +1 -0
- package/dist-server/service/quotation/quotation-event.d.ts +22 -0
- package/dist-server/service/quotation/quotation-event.js +98 -0
- package/dist-server/service/quotation/quotation-event.js.map +1 -0
- package/dist-server/service/quotation/quotation-item-query.d.ts +8 -0
- package/dist-server/service/quotation/quotation-item-query.js +45 -0
- package/dist-server/service/quotation/quotation-item-query.js.map +1 -0
- package/dist-server/service/quotation/quotation-item.d.ts +18 -0
- package/dist-server/service/quotation/quotation-item.js +75 -0
- package/dist-server/service/quotation/quotation-item.js.map +1 -0
- package/dist-server/service/quotation/quotation-mutation.d.ts +11 -0
- package/dist-server/service/quotation/quotation-mutation.js +117 -0
- package/dist-server/service/quotation/quotation-mutation.js.map +1 -0
- package/dist-server/service/quotation/quotation-query.d.ts +17 -0
- package/dist-server/service/quotation/quotation-query.js +125 -0
- package/dist-server/service/quotation/quotation-query.js.map +1 -0
- package/dist-server/service/quotation/quotation-rules.d.ts +6 -0
- package/dist-server/service/quotation/quotation-rules.js +22 -0
- package/dist-server/service/quotation/quotation-rules.js.map +1 -0
- package/dist-server/service/quotation/quotation-service.d.ts +23 -0
- package/dist-server/service/quotation/quotation-service.js +115 -0
- package/dist-server/service/quotation/quotation-service.js.map +1 -0
- package/dist-server/service/quotation/quotation-type.d.ts +9 -0
- package/dist-server/service/quotation/quotation-type.js +32 -0
- package/dist-server/service/quotation/quotation-type.js.map +1 -0
- package/dist-server/service/quotation/quotation.d.ts +33 -0
- package/dist-server/service/quotation/quotation.js +109 -0
- package/dist-server/service/quotation/quotation.js.map +1 -0
- package/dist-server/service/sales-event.d.ts +15 -0
- package/dist-server/service/sales-event.js +12 -0
- package/dist-server/service/sales-event.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/helps/quotation/quotation.md +160 -0
- package/package.json +46 -4
- package/things-factory.config.js +13 -0
- package/translations/en.json +98 -0
- package/translations/es.json +98 -0
- package/translations/ja.json +98 -0
- package/translations/ko.json +98 -0
- package/translations/ms.json +98 -0
- package/translations/zh.json +98 -0
- package/README.md +0 -3
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
import '@operato/list-state/ox-page-state.js';
|
|
3
|
+
import gql from 'graphql-tag';
|
|
4
|
+
import { css, html, nothing } from 'lit';
|
|
5
|
+
import { customElement, state } from 'lit/decorators.js';
|
|
6
|
+
import { client } from '@operato/graphql';
|
|
7
|
+
import { i18next, localize } from '@operato/i18n';
|
|
8
|
+
import { notify } from '@operato/layout';
|
|
9
|
+
import { navigate, PageView } from '@operato/shell';
|
|
10
|
+
import { ButtonStyles, DocumentPageStyles, FormFieldStyles } from '@operato/styles';
|
|
11
|
+
import { currencyChoices } from '@things-factory/invoice/dist-client';
|
|
12
|
+
import { opportunitySection } from '../opportunity-attach';
|
|
13
|
+
import { opportunityStageWord } from '../opportunity-words';
|
|
14
|
+
const FIELDS = `
|
|
15
|
+
id name stage probability amount currency expectedCloseMonth closedOn lostReason description
|
|
16
|
+
counterpartyId endCustomerId ownerId counterparty { id name } endCustomer { id name } owner { id name }
|
|
17
|
+
`;
|
|
18
|
+
const thisMonth = () => new Date().toLocaleDateString('en-CA').slice(0, 7);
|
|
19
|
+
/**
|
|
20
|
+
* 영업 기회 편집 — with whom, for how much, by when and how likely.
|
|
21
|
+
*
|
|
22
|
+
* A person moves an open opportunity through the stages; changing the stage puts that stage's probability in, and the
|
|
23
|
+
* person may change it. An open one is closed as lost with the reason; a lost one is opened again by choosing an open
|
|
24
|
+
* stage. Winning is the application's: it closes the opportunity when the opportunity becomes its contract.
|
|
25
|
+
*/
|
|
26
|
+
let OpportunityEditor = class OpportunityEditor extends localize(i18next)(PageView) {
|
|
27
|
+
constructor() {
|
|
28
|
+
super(...arguments);
|
|
29
|
+
this.opportunity = null;
|
|
30
|
+
this.form = blank();
|
|
31
|
+
this.pickers = { counterparty: { search: '', found: [] }, endCustomer: { search: '', found: [] } };
|
|
32
|
+
this.stages = [];
|
|
33
|
+
this.owners = [];
|
|
34
|
+
this.events = [];
|
|
35
|
+
this.reason = '';
|
|
36
|
+
this.loading = false;
|
|
37
|
+
this.error = null;
|
|
38
|
+
this.busy = false;
|
|
39
|
+
}
|
|
40
|
+
static { this.styles = [
|
|
41
|
+
FormFieldStyles,
|
|
42
|
+
ButtonStyles,
|
|
43
|
+
DocumentPageStyles,
|
|
44
|
+
css `
|
|
45
|
+
.party {
|
|
46
|
+
display: flex;
|
|
47
|
+
gap: var(--spacing-small);
|
|
48
|
+
align-items: center;
|
|
49
|
+
}
|
|
50
|
+
.party input {
|
|
51
|
+
flex: 1;
|
|
52
|
+
}
|
|
53
|
+
table {
|
|
54
|
+
width: 100%;
|
|
55
|
+
border-collapse: collapse;
|
|
56
|
+
font-variant-numeric: tabular-nums;
|
|
57
|
+
}
|
|
58
|
+
th,
|
|
59
|
+
td {
|
|
60
|
+
text-align: left;
|
|
61
|
+
padding: var(--spacing-small);
|
|
62
|
+
border-bottom: 1px solid var(--ui-border, rgba(0, 0, 0, 0.1));
|
|
63
|
+
}
|
|
64
|
+
td.number {
|
|
65
|
+
text-align: right;
|
|
66
|
+
}
|
|
67
|
+
`
|
|
68
|
+
]; }
|
|
69
|
+
isOpen(stage) {
|
|
70
|
+
return this.stages.some(one => one.stage === stage && one.open);
|
|
71
|
+
}
|
|
72
|
+
/** Won is final; a lost one is changed only by being opened again. */
|
|
73
|
+
get editable() {
|
|
74
|
+
return !this.busy && this.opportunity?.stage !== 'WON' && this.isOpen(this.form.stage);
|
|
75
|
+
}
|
|
76
|
+
get context() {
|
|
77
|
+
const actions = [];
|
|
78
|
+
if (this.editable)
|
|
79
|
+
actions.push({ title: i18next.t('button.save'), icon: 'save', emphasis: { raised: true }, action: () => this.save() });
|
|
80
|
+
return {
|
|
81
|
+
title: this.opportunity ? `${i18next.t('title.opportunity')} ${this.opportunity.name}` : i18next.t('title.opportunity-new'),
|
|
82
|
+
...(this.opportunity ? { description: opportunityStageWord(this.opportunity.stage) } : {}),
|
|
83
|
+
frame: 'document',
|
|
84
|
+
/* Opened from the opportunity list; the menu marks it. */
|
|
85
|
+
menu: 'opportunity-list',
|
|
86
|
+
actions: this.error ? [] : actions
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
async pageUpdated(changes, lifecycle) {
|
|
90
|
+
if (!this.active || !(changes.active || changes.params))
|
|
91
|
+
return;
|
|
92
|
+
if (!this.stages.length)
|
|
93
|
+
await this.readChoices();
|
|
94
|
+
const id = lifecycle?.params?.id;
|
|
95
|
+
if (!id) {
|
|
96
|
+
this.opportunity = null;
|
|
97
|
+
this.form = blank(this.stages);
|
|
98
|
+
this.events = [];
|
|
99
|
+
this.updateContext();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (id !== this.opportunity?.id)
|
|
103
|
+
await this.load(id);
|
|
104
|
+
}
|
|
105
|
+
render() {
|
|
106
|
+
const attached = this.opportunity ? opportunitySection() : undefined;
|
|
107
|
+
const editable = this.editable;
|
|
108
|
+
const set = (patch) => (this.form = { ...this.form, ...patch });
|
|
109
|
+
const stageChoices = this.stages.filter(one => one.open || one.stage === this.opportunity?.stage);
|
|
110
|
+
return html `<ox-page-state .loading=${this.loading} .error=${this.error} @retry=${() => this.opportunity && this.load(this.opportunity.id)}>
|
|
111
|
+
<div class="page">
|
|
112
|
+
<section class="page-section">
|
|
113
|
+
<header class="page-section-header"><h3>${i18next.t('text.opportunity-section-head')}</h3></header>
|
|
114
|
+
<div class="form">
|
|
115
|
+
<label class="wide"
|
|
116
|
+
>${i18next.t('field.opportunity-name')}
|
|
117
|
+
<input .value=${this.form.name} ?disabled=${!editable} @input=${(e) => set({ name: e.target.value })}
|
|
118
|
+
/></label>
|
|
119
|
+
${this.renderParty('counterparty', i18next.t('field.opportunity-counterparty'), editable)}
|
|
120
|
+
${this.renderParty('endCustomer', i18next.t('field.opportunity-end-customer'), editable)}
|
|
121
|
+
<label
|
|
122
|
+
>${i18next.t('field.opportunity-amount')}
|
|
123
|
+
<input type="number" min="0" .value=${this.form.amount} ?disabled=${!editable} @input=${(e) => set({ amount: e.target.value })}
|
|
124
|
+
/></label>
|
|
125
|
+
<label
|
|
126
|
+
>${i18next.t('field.opportunity-currency')}
|
|
127
|
+
<select .value=${this.form.currency} ?disabled=${!editable} @change=${(e) => set({ currency: e.target.value })}>
|
|
128
|
+
${currencyChoices().map((code) => html `<option value=${code} ?selected=${code === this.form.currency}>${code}</option>`)}
|
|
129
|
+
</select>
|
|
130
|
+
</label>
|
|
131
|
+
<label
|
|
132
|
+
>${i18next.t('field.opportunity-expected-close-month')}
|
|
133
|
+
<input type="month" .value=${this.form.expectedCloseMonth} ?disabled=${!editable} @input=${(e) => set({ expectedCloseMonth: e.target.value })}
|
|
134
|
+
/></label>
|
|
135
|
+
<label
|
|
136
|
+
>${i18next.t('field.opportunity-owner')}
|
|
137
|
+
<select .value=${this.form.ownerId} ?disabled=${!editable} @change=${(e) => set({ ownerId: e.target.value })}>
|
|
138
|
+
${this.form.ownerId ? nothing : html `<option value="" selected>${i18next.t('text.opportunity-owner-me')}</option>`}
|
|
139
|
+
${this.owners.map(one => html `<option value=${one.id} ?selected=${one.id === this.form.ownerId}>${one.name}</option>`)}
|
|
140
|
+
</select>
|
|
141
|
+
</label>
|
|
142
|
+
<label class="wide"
|
|
143
|
+
>${i18next.t('field.opportunity-description')}
|
|
144
|
+
<textarea .value=${this.form.description} ?disabled=${!editable} @input=${(e) => set({ description: e.target.value })}></textarea>
|
|
145
|
+
</label>
|
|
146
|
+
</div>
|
|
147
|
+
</section>
|
|
148
|
+
<section class="page-section">
|
|
149
|
+
<header class="page-section-header"><h3>${i18next.t('text.opportunity-section-stage')}</h3></header>
|
|
150
|
+
<div class="form">
|
|
151
|
+
<label
|
|
152
|
+
>${i18next.t('field.opportunity-stage')}
|
|
153
|
+
<select .value=${this.form.stage} ?disabled=${this.busy || this.opportunity?.stage === 'WON'} @change=${(e) => this.chooseStage(e.target.value)}>
|
|
154
|
+
${stageChoices.map(one => html `<option value=${one.stage} ?selected=${one.stage === this.form.stage} ?disabled=${!one.open}>${opportunityStageWord(one.stage)}</option>`)}
|
|
155
|
+
</select>
|
|
156
|
+
</label>
|
|
157
|
+
<label
|
|
158
|
+
>${i18next.t('field.opportunity-probability')}
|
|
159
|
+
<input type="number" min="0" max="100" step="1" .value=${this.form.probability} ?disabled=${!editable} @input=${(e) => set({ probability: e.target.value })}
|
|
160
|
+
/></label>
|
|
161
|
+
${this.opportunity?.closedOn
|
|
162
|
+
? html `<label>${i18next.t('field.opportunity-closed-on')} <strong>${this.opportunity.closedOn}</strong></label>`
|
|
163
|
+
: nothing}
|
|
164
|
+
</div>
|
|
165
|
+
</section>
|
|
166
|
+
${this.opportunity && this.isOpen(this.opportunity.stage) ? this.renderLose() : nothing}
|
|
167
|
+
${this.opportunity?.lostReason
|
|
168
|
+
? html `<section class="page-section">
|
|
169
|
+
<header class="page-section-header"><h3>${i18next.t('field.opportunity-lost-reason')}</h3></header>
|
|
170
|
+
<p>${this.opportunity.lostReason}</p>
|
|
171
|
+
</section>`
|
|
172
|
+
: nothing}
|
|
173
|
+
${attached && this.opportunity
|
|
174
|
+
? html `<section class="page-section">
|
|
175
|
+
<header class="page-section-header"><h3>${attached.heading()}</h3></header>
|
|
176
|
+
${attached.render(this.opportunity)}
|
|
177
|
+
</section>`
|
|
178
|
+
: nothing}
|
|
179
|
+
${this.events.length ? this.renderEvents() : nothing}
|
|
180
|
+
</div>
|
|
181
|
+
</ox-page-state>`;
|
|
182
|
+
}
|
|
183
|
+
/** A counterparty of the domain, found by name or business number. */
|
|
184
|
+
renderParty(which, label, editable) {
|
|
185
|
+
const picker = this.pickers[which];
|
|
186
|
+
const chosen = this.form[which];
|
|
187
|
+
return html `<label
|
|
188
|
+
>${label}
|
|
189
|
+
<span class="party">
|
|
190
|
+
<input
|
|
191
|
+
.value=${picker.search}
|
|
192
|
+
placeholder=${chosen?.name ?? i18next.t('text.opportunity-counterparty-search')}
|
|
193
|
+
?disabled=${!editable}
|
|
194
|
+
@input=${(e) => this.setPicker(which, { search: e.target.value })}
|
|
195
|
+
@keydown=${(e) => e.key === 'Enter' && this.findParties(which)}
|
|
196
|
+
/>
|
|
197
|
+
<button ?disabled=${!editable} @click=${() => this.findParties(which)}>${i18next.t('button.search')}</button>
|
|
198
|
+
${which === 'endCustomer' && chosen ? html `<button ?disabled=${!editable} @click=${() => (this.form = { ...this.form, endCustomer: null })}>${i18next.t('button.opportunity-clear')}</button>` : nothing}
|
|
199
|
+
</span>
|
|
200
|
+
${picker.found.length
|
|
201
|
+
? html `<select ?disabled=${!editable} @change=${(e) => this.chooseParty(which, picker.found.find(one => one.id === e.target.value) ?? null)}>
|
|
202
|
+
<option value="">${chosen?.name ?? ''}</option>
|
|
203
|
+
${picker.found.map(one => html `<option value=${one.id}>${one.name}${one.businessNumber ? ` (${one.businessNumber})` : ''}</option>`)}
|
|
204
|
+
</select>`
|
|
205
|
+
: nothing}
|
|
206
|
+
</label>`;
|
|
207
|
+
}
|
|
208
|
+
renderLose() {
|
|
209
|
+
return html `<section class="page-section">
|
|
210
|
+
<header class="page-section-header">
|
|
211
|
+
<h3>${i18next.t('text.opportunity-section-lose')}</h3>
|
|
212
|
+
<div class="page-section-actions">
|
|
213
|
+
<button ?disabled=${this.busy || !this.reason.trim()} @click=${() => this.lose()}>${i18next.t('button.opportunity-lose')}</button>
|
|
214
|
+
</div>
|
|
215
|
+
</header>
|
|
216
|
+
<div class="form">
|
|
217
|
+
<label class="wide"
|
|
218
|
+
>${i18next.t('field.opportunity-lost-reason')}
|
|
219
|
+
<input .value=${this.reason} ?disabled=${this.busy} @input=${(e) => (this.reason = e.target.value)}
|
|
220
|
+
/></label>
|
|
221
|
+
</div>
|
|
222
|
+
</section>`;
|
|
223
|
+
}
|
|
224
|
+
renderEvents() {
|
|
225
|
+
return html `<section class="page-section">
|
|
226
|
+
<header class="page-section-header"><h3>${i18next.t('text.opportunity-section-history')}</h3></header>
|
|
227
|
+
<table>
|
|
228
|
+
<thead>
|
|
229
|
+
<tr>
|
|
230
|
+
<th>${i18next.t('field.opportunity-event-at')}</th>
|
|
231
|
+
<th>${i18next.t('field.opportunity-event-actor')}</th>
|
|
232
|
+
<th>${i18next.t('field.opportunity-event-action')}</th>
|
|
233
|
+
<th>${i18next.t('field.opportunity-stage')}</th>
|
|
234
|
+
<th>${i18next.t('field.opportunity-probability')}</th>
|
|
235
|
+
</tr>
|
|
236
|
+
</thead>
|
|
237
|
+
<tbody>
|
|
238
|
+
${this.events.map(one => html `<tr>
|
|
239
|
+
<td>${one.createdAt ? new Date(one.createdAt).toLocaleString() : ''}</td>
|
|
240
|
+
<td>${one.actor?.name ?? ''}</td>
|
|
241
|
+
<td>${eventWord(one.action)}${one.reason ? ` · ${one.reason}` : ''}</td>
|
|
242
|
+
<td>${one.fromStage ? `${opportunityStageWord(one.fromStage)} → ` : ''}${opportunityStageWord(one.stage)}</td>
|
|
243
|
+
<td class="number">${one.probability}%</td>
|
|
244
|
+
</tr>`)}
|
|
245
|
+
</tbody>
|
|
246
|
+
</table>
|
|
247
|
+
</section>`;
|
|
248
|
+
}
|
|
249
|
+
setPicker(which, patch) {
|
|
250
|
+
this.pickers = { ...this.pickers, [which]: { ...this.pickers[which], ...patch } };
|
|
251
|
+
}
|
|
252
|
+
chooseParty(which, party) {
|
|
253
|
+
if (!party)
|
|
254
|
+
return;
|
|
255
|
+
this.form = { ...this.form, [which]: party };
|
|
256
|
+
this.setPicker(which, { search: '', found: [] });
|
|
257
|
+
}
|
|
258
|
+
/** The stage's own probability comes in with it; the person may change it afterwards. */
|
|
259
|
+
chooseStage(stage) {
|
|
260
|
+
const given = this.stages.find(one => one.stage === stage);
|
|
261
|
+
this.form = { ...this.form, stage, probability: given ? String(given.probability) : this.form.probability };
|
|
262
|
+
}
|
|
263
|
+
async readChoices() {
|
|
264
|
+
try {
|
|
265
|
+
const response = await client.query({
|
|
266
|
+
query: gql `
|
|
267
|
+
query {
|
|
268
|
+
opportunityStages { stage probability open }
|
|
269
|
+
opportunityOwners { id name email }
|
|
270
|
+
}
|
|
271
|
+
`,
|
|
272
|
+
fetchPolicy: 'no-cache'
|
|
273
|
+
});
|
|
274
|
+
if (response.errors?.length)
|
|
275
|
+
throw response;
|
|
276
|
+
this.stages = response.data.opportunityStages;
|
|
277
|
+
this.owners = response.data.opportunityOwners;
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
this.error = error;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
async findParties(which) {
|
|
284
|
+
await this.run(async () => {
|
|
285
|
+
const response = await client.query({
|
|
286
|
+
query: gql `
|
|
287
|
+
query ($search: String, $pagination: Pagination) {
|
|
288
|
+
counterparties(search: $search, pagination: $pagination) {
|
|
289
|
+
items { id name businessNumber }
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
`,
|
|
293
|
+
variables: { search: this.pickers[which].search.trim() || undefined, pagination: { page: 1, limit: 30 } },
|
|
294
|
+
fetchPolicy: 'no-cache'
|
|
295
|
+
});
|
|
296
|
+
if (response.errors?.length)
|
|
297
|
+
throw response;
|
|
298
|
+
const found = response.data.counterparties.items ?? [];
|
|
299
|
+
if (found.length === 1)
|
|
300
|
+
this.chooseParty(which, found[0]);
|
|
301
|
+
else
|
|
302
|
+
this.setPicker(which, { found });
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
async load(id) {
|
|
306
|
+
this.loading = true;
|
|
307
|
+
this.error = null;
|
|
308
|
+
try {
|
|
309
|
+
const response = await client.query({
|
|
310
|
+
query: gql(`query ($id: String!) { opportunity(id: $id) { ${FIELDS} } opportunityEvents(opportunityId: $id) { id action fromStage stage probability reason createdAt actor { id name } } }`),
|
|
311
|
+
variables: { id },
|
|
312
|
+
fetchPolicy: 'no-cache'
|
|
313
|
+
});
|
|
314
|
+
if (response.errors?.length)
|
|
315
|
+
throw response;
|
|
316
|
+
if (!response.data.opportunity)
|
|
317
|
+
throw new Error(i18next.t('text.opportunity-not-found'));
|
|
318
|
+
this.show(response.data.opportunity, response.data.opportunityEvents);
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
this.error = error;
|
|
322
|
+
}
|
|
323
|
+
finally {
|
|
324
|
+
this.loading = false;
|
|
325
|
+
this.updateContext();
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
show(opportunity, events) {
|
|
329
|
+
this.opportunity = opportunity;
|
|
330
|
+
this.events = events;
|
|
331
|
+
this.form = {
|
|
332
|
+
name: opportunity.name,
|
|
333
|
+
counterparty: opportunity.counterparty,
|
|
334
|
+
endCustomer: opportunity.endCustomer,
|
|
335
|
+
amount: String(opportunity.amount ?? 0),
|
|
336
|
+
currency: opportunity.currency,
|
|
337
|
+
expectedCloseMonth: opportunity.expectedCloseMonth,
|
|
338
|
+
stage: opportunity.stage,
|
|
339
|
+
probability: String(opportunity.probability),
|
|
340
|
+
ownerId: opportunity.ownerId ?? '',
|
|
341
|
+
description: opportunity.description ?? ''
|
|
342
|
+
};
|
|
343
|
+
this.reason = '';
|
|
344
|
+
}
|
|
345
|
+
async run(work) {
|
|
346
|
+
if (this.busy)
|
|
347
|
+
return undefined;
|
|
348
|
+
this.busy = true;
|
|
349
|
+
try {
|
|
350
|
+
return await work();
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
notify({ level: 'error', message: error?.graphQLErrors?.[0]?.message ?? error?.errors?.[0]?.message ?? error?.message ?? String(error) });
|
|
354
|
+
return undefined;
|
|
355
|
+
}
|
|
356
|
+
finally {
|
|
357
|
+
this.busy = false;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
input() {
|
|
361
|
+
const { name, counterparty, endCustomer, amount, currency, expectedCloseMonth, stage, probability, ownerId, description } = this.form;
|
|
362
|
+
return {
|
|
363
|
+
name,
|
|
364
|
+
counterpartyId: counterparty?.id ?? '',
|
|
365
|
+
endCustomerId: endCustomer?.id ?? null,
|
|
366
|
+
amount: Number(amount || 0),
|
|
367
|
+
currency,
|
|
368
|
+
expectedCloseMonth,
|
|
369
|
+
stage,
|
|
370
|
+
probability: probability === '' ? null : Number(probability),
|
|
371
|
+
ownerId: ownerId || null,
|
|
372
|
+
description
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
async save() {
|
|
376
|
+
const id = this.opportunity?.id;
|
|
377
|
+
const saved = await this.run(async () => {
|
|
378
|
+
const response = await client.mutate({
|
|
379
|
+
mutation: gql(id
|
|
380
|
+
? `mutation ($id: String!, $opportunity: NewOpportunity!) { opportunity: updateOpportunity(id: $id, opportunity: $opportunity) { id } }`
|
|
381
|
+
: `mutation ($opportunity: NewOpportunity!) { opportunity: createOpportunity(opportunity: $opportunity) { id } }`),
|
|
382
|
+
variables: { ...(id ? { id } : {}), opportunity: this.input() }
|
|
383
|
+
});
|
|
384
|
+
if (response.errors?.length)
|
|
385
|
+
throw response;
|
|
386
|
+
return response.data.opportunity;
|
|
387
|
+
});
|
|
388
|
+
if (!saved)
|
|
389
|
+
return;
|
|
390
|
+
if (!id)
|
|
391
|
+
navigate(`opportunity-editor?id=${saved.id}`);
|
|
392
|
+
else
|
|
393
|
+
await this.load(id);
|
|
394
|
+
}
|
|
395
|
+
async lose() {
|
|
396
|
+
const id = this.opportunity.id;
|
|
397
|
+
const lost = await this.run(async () => {
|
|
398
|
+
const response = await client.mutate({
|
|
399
|
+
mutation: gql `
|
|
400
|
+
mutation ($id: String!, $reason: String!) {
|
|
401
|
+
loseOpportunity(id: $id, reason: $reason) { id }
|
|
402
|
+
}
|
|
403
|
+
`,
|
|
404
|
+
variables: { id, reason: this.reason.trim() }
|
|
405
|
+
});
|
|
406
|
+
if (response.errors?.length)
|
|
407
|
+
throw response;
|
|
408
|
+
return response.data.loseOpportunity;
|
|
409
|
+
});
|
|
410
|
+
if (lost)
|
|
411
|
+
await this.load(id);
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
__decorate([
|
|
415
|
+
state(),
|
|
416
|
+
__metadata("design:type", Object)
|
|
417
|
+
], OpportunityEditor.prototype, "opportunity", void 0);
|
|
418
|
+
__decorate([
|
|
419
|
+
state(),
|
|
420
|
+
__metadata("design:type", Object)
|
|
421
|
+
], OpportunityEditor.prototype, "form", void 0);
|
|
422
|
+
__decorate([
|
|
423
|
+
state(),
|
|
424
|
+
__metadata("design:type", Object)
|
|
425
|
+
], OpportunityEditor.prototype, "pickers", void 0);
|
|
426
|
+
__decorate([
|
|
427
|
+
state(),
|
|
428
|
+
__metadata("design:type", Array)
|
|
429
|
+
], OpportunityEditor.prototype, "stages", void 0);
|
|
430
|
+
__decorate([
|
|
431
|
+
state(),
|
|
432
|
+
__metadata("design:type", Array)
|
|
433
|
+
], OpportunityEditor.prototype, "owners", void 0);
|
|
434
|
+
__decorate([
|
|
435
|
+
state(),
|
|
436
|
+
__metadata("design:type", Array)
|
|
437
|
+
], OpportunityEditor.prototype, "events", void 0);
|
|
438
|
+
__decorate([
|
|
439
|
+
state(),
|
|
440
|
+
__metadata("design:type", Object)
|
|
441
|
+
], OpportunityEditor.prototype, "reason", void 0);
|
|
442
|
+
__decorate([
|
|
443
|
+
state(),
|
|
444
|
+
__metadata("design:type", Object)
|
|
445
|
+
], OpportunityEditor.prototype, "loading", void 0);
|
|
446
|
+
__decorate([
|
|
447
|
+
state(),
|
|
448
|
+
__metadata("design:type", Object)
|
|
449
|
+
], OpportunityEditor.prototype, "error", void 0);
|
|
450
|
+
__decorate([
|
|
451
|
+
state(),
|
|
452
|
+
__metadata("design:type", Object)
|
|
453
|
+
], OpportunityEditor.prototype, "busy", void 0);
|
|
454
|
+
OpportunityEditor = __decorate([
|
|
455
|
+
customElement('opportunity-editor')
|
|
456
|
+
], OpportunityEditor);
|
|
457
|
+
export { OpportunityEditor };
|
|
458
|
+
const eventWord = (action) => action === 'CREATED'
|
|
459
|
+
? i18next.t('text.opportunity-event-created')
|
|
460
|
+
: action === 'STAGED'
|
|
461
|
+
? i18next.t('text.opportunity-event-staged')
|
|
462
|
+
: action === 'WON'
|
|
463
|
+
? i18next.t('text.opportunity-event-won')
|
|
464
|
+
: action === 'LOST'
|
|
465
|
+
? i18next.t('text.opportunity-event-lost')
|
|
466
|
+
: i18next.t('text.opportunity-event-updated');
|
|
467
|
+
function blank(stages = []) {
|
|
468
|
+
const lead = stages.find(one => one.stage === 'LEAD');
|
|
469
|
+
return {
|
|
470
|
+
name: '',
|
|
471
|
+
counterparty: null,
|
|
472
|
+
endCustomer: null,
|
|
473
|
+
amount: '0',
|
|
474
|
+
currency: 'KRW',
|
|
475
|
+
expectedCloseMonth: thisMonth(),
|
|
476
|
+
stage: 'LEAD',
|
|
477
|
+
probability: lead ? String(lead.probability) : '',
|
|
478
|
+
ownerId: '',
|
|
479
|
+
description: ''
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
//# sourceMappingURL=opportunity-editor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opportunity-editor.js","sourceRoot":"","sources":["../../client/pages/opportunity-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/list-state/ox-page-state.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, nothing } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\n\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { notify } from '@operato/layout'\nimport { navigate, PageView } from '@operato/shell'\nimport { ButtonStyles, DocumentPageStyles, FormFieldStyles } from '@operato/styles'\nimport { currencyChoices } from '@things-factory/invoice/dist-client'\n\nimport { opportunitySection } from '../opportunity-attach'\nimport { opportunityStageWord } from '../opportunity-words'\n\nconst FIELDS = `\n id name stage probability amount currency expectedCloseMonth closedOn lostReason description\n counterpartyId endCustomerId ownerId counterparty { id name } endCustomer { id name } owner { id name }\n`\n\ntype Party = { id: string; name: string; businessNumber?: string }\ntype Picker = { search: string; found: Party[] }\ntype Form = {\n name: string\n counterparty: Party | null\n endCustomer: Party | null\n amount: string\n currency: string\n expectedCloseMonth: string\n stage: string\n probability: string\n ownerId: string\n description: string\n}\n\nconst thisMonth = () => new Date().toLocaleDateString('en-CA').slice(0, 7)\n\n/**\n * 영업 기회 편집 — with whom, for how much, by when and how likely.\n *\n * A person moves an open opportunity through the stages; changing the stage puts that stage's probability in, and the\n * person may change it. An open one is closed as lost with the reason; a lost one is opened again by choosing an open\n * stage. Winning is the application's: it closes the opportunity when the opportunity becomes its contract.\n */\n@customElement('opportunity-editor')\nexport class OpportunityEditor extends localize(i18next)(PageView) {\n static styles = [\n FormFieldStyles,\n ButtonStyles,\n DocumentPageStyles,\n css`\n .party {\n display: flex;\n gap: var(--spacing-small);\n align-items: center;\n }\n .party input {\n flex: 1;\n }\n table {\n width: 100%;\n border-collapse: collapse;\n font-variant-numeric: tabular-nums;\n }\n th,\n td {\n text-align: left;\n padding: var(--spacing-small);\n border-bottom: 1px solid var(--ui-border, rgba(0, 0, 0, 0.1));\n }\n td.number {\n text-align: right;\n }\n `\n ]\n\n @state() opportunity: any = null\n @state() form: Form = blank()\n @state() pickers: Record<'counterparty' | 'endCustomer', Picker> = { counterparty: { search: '', found: [] }, endCustomer: { search: '', found: [] } }\n @state() stages: { stage: string; probability: number; open: boolean }[] = []\n @state() owners: { id: string; name: string; email?: string }[] = []\n @state() events: any[] = []\n @state() reason = ''\n @state() loading = false\n @state() error: unknown = null\n @state() busy = false\n\n private isOpen(stage: string) {\n return this.stages.some(one => one.stage === stage && one.open)\n }\n\n /** Won is final; a lost one is changed only by being opened again. */\n get editable() {\n return !this.busy && this.opportunity?.stage !== 'WON' && this.isOpen(this.form.stage)\n }\n\n get context() {\n const actions: any[] = []\n if (this.editable) actions.push({ title: i18next.t('button.save'), icon: 'save', emphasis: { raised: true }, action: () => this.save() })\n return {\n title: this.opportunity ? `${i18next.t('title.opportunity')} ${this.opportunity.name}` : i18next.t('title.opportunity-new'),\n ...(this.opportunity ? { description: opportunityStageWord(this.opportunity.stage) } : {}),\n frame: 'document',\n /* Opened from the opportunity list; the menu marks it. */\n menu: 'opportunity-list',\n actions: this.error ? [] : actions\n }\n }\n\n async pageUpdated(changes: any, lifecycle: any) {\n if (!this.active || !(changes.active || changes.params)) return\n if (!this.stages.length) await this.readChoices()\n const id = lifecycle?.params?.id\n if (!id) {\n this.opportunity = null\n this.form = blank(this.stages)\n this.events = []\n this.updateContext()\n return\n }\n if (id !== this.opportunity?.id) await this.load(id)\n }\n\n render() {\n const attached = this.opportunity ? opportunitySection() : undefined\n const editable = this.editable\n const set = (patch: Partial<Form>) => (this.form = { ...this.form, ...patch })\n const stageChoices = this.stages.filter(one => one.open || one.stage === this.opportunity?.stage)\n return html`<ox-page-state .loading=${this.loading} .error=${this.error} @retry=${() => this.opportunity && this.load(this.opportunity.id)}>\n <div class=\"page\">\n <section class=\"page-section\">\n <header class=\"page-section-header\"><h3>${i18next.t('text.opportunity-section-head')}</h3></header>\n <div class=\"form\">\n <label class=\"wide\"\n >${i18next.t('field.opportunity-name')}\n <input .value=${this.form.name} ?disabled=${!editable} @input=${(e: any) => set({ name: e.target.value })}\n /></label>\n ${this.renderParty('counterparty', i18next.t('field.opportunity-counterparty'), editable)}\n ${this.renderParty('endCustomer', i18next.t('field.opportunity-end-customer'), editable)}\n <label\n >${i18next.t('field.opportunity-amount')}\n <input type=\"number\" min=\"0\" .value=${this.form.amount} ?disabled=${!editable} @input=${(e: any) => set({ amount: e.target.value })}\n /></label>\n <label\n >${i18next.t('field.opportunity-currency')}\n <select .value=${this.form.currency} ?disabled=${!editable} @change=${(e: any) => set({ currency: e.target.value })}>\n ${currencyChoices().map((code: string) => html`<option value=${code} ?selected=${code === this.form.currency}>${code}</option>`)}\n </select>\n </label>\n <label\n >${i18next.t('field.opportunity-expected-close-month')}\n <input type=\"month\" .value=${this.form.expectedCloseMonth} ?disabled=${!editable} @input=${(e: any) => set({ expectedCloseMonth: e.target.value })}\n /></label>\n <label\n >${i18next.t('field.opportunity-owner')}\n <select .value=${this.form.ownerId} ?disabled=${!editable} @change=${(e: any) => set({ ownerId: e.target.value })}>\n ${this.form.ownerId ? nothing : html`<option value=\"\" selected>${i18next.t('text.opportunity-owner-me')}</option>`}\n ${this.owners.map(one => html`<option value=${one.id} ?selected=${one.id === this.form.ownerId}>${one.name}</option>`)}\n </select>\n </label>\n <label class=\"wide\"\n >${i18next.t('field.opportunity-description')}\n <textarea .value=${this.form.description} ?disabled=${!editable} @input=${(e: any) => set({ description: e.target.value })}></textarea>\n </label>\n </div>\n </section>\n <section class=\"page-section\">\n <header class=\"page-section-header\"><h3>${i18next.t('text.opportunity-section-stage')}</h3></header>\n <div class=\"form\">\n <label\n >${i18next.t('field.opportunity-stage')}\n <select .value=${this.form.stage} ?disabled=${this.busy || this.opportunity?.stage === 'WON'} @change=${(e: any) => this.chooseStage(e.target.value)}>\n ${stageChoices.map(one => html`<option value=${one.stage} ?selected=${one.stage === this.form.stage} ?disabled=${!one.open}>${opportunityStageWord(one.stage)}</option>`)}\n </select>\n </label>\n <label\n >${i18next.t('field.opportunity-probability')}\n <input type=\"number\" min=\"0\" max=\"100\" step=\"1\" .value=${this.form.probability} ?disabled=${!editable} @input=${(e: any) => set({ probability: e.target.value })}\n /></label>\n ${this.opportunity?.closedOn\n ? html`<label>${i18next.t('field.opportunity-closed-on')} <strong>${this.opportunity.closedOn}</strong></label>`\n : nothing}\n </div>\n </section>\n ${this.opportunity && this.isOpen(this.opportunity.stage) ? this.renderLose() : nothing}\n ${this.opportunity?.lostReason\n ? html`<section class=\"page-section\">\n <header class=\"page-section-header\"><h3>${i18next.t('field.opportunity-lost-reason')}</h3></header>\n <p>${this.opportunity.lostReason}</p>\n </section>`\n : nothing}\n ${attached && this.opportunity\n ? html`<section class=\"page-section\">\n <header class=\"page-section-header\"><h3>${attached.heading()}</h3></header>\n ${attached.render(this.opportunity)}\n </section>`\n : nothing}\n ${this.events.length ? this.renderEvents() : nothing}\n </div>\n </ox-page-state>`\n }\n\n /** A counterparty of the domain, found by name or business number. */\n private renderParty(which: 'counterparty' | 'endCustomer', label: string, editable: boolean) {\n const picker = this.pickers[which]\n const chosen = this.form[which]\n return html`<label\n >${label}\n <span class=\"party\">\n <input\n .value=${picker.search}\n placeholder=${chosen?.name ?? i18next.t('text.opportunity-counterparty-search')}\n ?disabled=${!editable}\n @input=${(e: any) => this.setPicker(which, { search: e.target.value })}\n @keydown=${(e: KeyboardEvent) => e.key === 'Enter' && this.findParties(which)}\n />\n <button ?disabled=${!editable} @click=${() => this.findParties(which)}>${i18next.t('button.search')}</button>\n ${which === 'endCustomer' && chosen ? html`<button ?disabled=${!editable} @click=${() => (this.form = { ...this.form, endCustomer: null })}>${i18next.t('button.opportunity-clear')}</button>` : nothing}\n </span>\n ${picker.found.length\n ? html`<select ?disabled=${!editable} @change=${(e: any) => this.chooseParty(which, picker.found.find(one => one.id === e.target.value) ?? null)}>\n <option value=\"\">${chosen?.name ?? ''}</option>\n ${picker.found.map(one => html`<option value=${one.id}>${one.name}${one.businessNumber ? ` (${one.businessNumber})` : ''}</option>`)}\n </select>`\n : nothing}\n </label>`\n }\n\n private renderLose() {\n return html`<section class=\"page-section\">\n <header class=\"page-section-header\">\n <h3>${i18next.t('text.opportunity-section-lose')}</h3>\n <div class=\"page-section-actions\">\n <button ?disabled=${this.busy || !this.reason.trim()} @click=${() => this.lose()}>${i18next.t('button.opportunity-lose')}</button>\n </div>\n </header>\n <div class=\"form\">\n <label class=\"wide\"\n >${i18next.t('field.opportunity-lost-reason')}\n <input .value=${this.reason} ?disabled=${this.busy} @input=${(e: any) => (this.reason = e.target.value)}\n /></label>\n </div>\n </section>`\n }\n\n private renderEvents() {\n return html`<section class=\"page-section\">\n <header class=\"page-section-header\"><h3>${i18next.t('text.opportunity-section-history')}</h3></header>\n <table>\n <thead>\n <tr>\n <th>${i18next.t('field.opportunity-event-at')}</th>\n <th>${i18next.t('field.opportunity-event-actor')}</th>\n <th>${i18next.t('field.opportunity-event-action')}</th>\n <th>${i18next.t('field.opportunity-stage')}</th>\n <th>${i18next.t('field.opportunity-probability')}</th>\n </tr>\n </thead>\n <tbody>\n ${this.events.map(\n one => html`<tr>\n <td>${one.createdAt ? new Date(one.createdAt).toLocaleString() : ''}</td>\n <td>${one.actor?.name ?? ''}</td>\n <td>${eventWord(one.action)}${one.reason ? ` · ${one.reason}` : ''}</td>\n <td>${one.fromStage ? `${opportunityStageWord(one.fromStage)} → ` : ''}${opportunityStageWord(one.stage)}</td>\n <td class=\"number\">${one.probability}%</td>\n </tr>`\n )}\n </tbody>\n </table>\n </section>`\n }\n\n private setPicker(which: 'counterparty' | 'endCustomer', patch: Partial<Picker>) {\n this.pickers = { ...this.pickers, [which]: { ...this.pickers[which], ...patch } }\n }\n\n private chooseParty(which: 'counterparty' | 'endCustomer', party: Party | null) {\n if (!party) return\n this.form = { ...this.form, [which]: party }\n this.setPicker(which, { search: '', found: [] })\n }\n\n /** The stage's own probability comes in with it; the person may change it afterwards. */\n private chooseStage(stage: string) {\n const given = this.stages.find(one => one.stage === stage)\n this.form = { ...this.form, stage, probability: given ? String(given.probability) : this.form.probability }\n }\n\n private async readChoices() {\n try {\n const response = await client.query({\n query: gql`\n query {\n opportunityStages { stage probability open }\n opportunityOwners { id name email }\n }\n `,\n fetchPolicy: 'no-cache'\n })\n if (response.errors?.length) throw response\n this.stages = response.data.opportunityStages\n this.owners = response.data.opportunityOwners\n } catch (error) {\n this.error = error\n }\n }\n\n private async findParties(which: 'counterparty' | 'endCustomer') {\n await this.run(async () => {\n const response = await client.query({\n query: gql`\n query ($search: String, $pagination: Pagination) {\n counterparties(search: $search, pagination: $pagination) {\n items { id name businessNumber }\n }\n }\n `,\n variables: { search: this.pickers[which].search.trim() || undefined, pagination: { page: 1, limit: 30 } },\n fetchPolicy: 'no-cache'\n })\n if (response.errors?.length) throw response\n const found: Party[] = response.data.counterparties.items ?? []\n if (found.length === 1) this.chooseParty(which, found[0])\n else this.setPicker(which, { found })\n })\n }\n\n private async load(id: string) {\n this.loading = true\n this.error = null\n try {\n const response = await client.query({\n query: gql(`query ($id: String!) { opportunity(id: $id) { ${FIELDS} } opportunityEvents(opportunityId: $id) { id action fromStage stage probability reason createdAt actor { id name } } }`),\n variables: { id },\n fetchPolicy: 'no-cache'\n })\n if (response.errors?.length) throw response\n if (!response.data.opportunity) throw new Error(i18next.t('text.opportunity-not-found'))\n this.show(response.data.opportunity, response.data.opportunityEvents)\n } catch (error) {\n this.error = error\n } finally {\n this.loading = false\n this.updateContext()\n }\n }\n\n private show(opportunity: any, events: any[]) {\n this.opportunity = opportunity\n this.events = events\n this.form = {\n name: opportunity.name,\n counterparty: opportunity.counterparty,\n endCustomer: opportunity.endCustomer,\n amount: String(opportunity.amount ?? 0),\n currency: opportunity.currency,\n expectedCloseMonth: opportunity.expectedCloseMonth,\n stage: opportunity.stage,\n probability: String(opportunity.probability),\n ownerId: opportunity.ownerId ?? '',\n description: opportunity.description ?? ''\n }\n this.reason = ''\n }\n\n private async run<T>(work: () => Promise<T>): Promise<T | undefined> {\n if (this.busy) return undefined\n this.busy = true\n try {\n return await work()\n } catch (error: any) {\n notify({ level: 'error', message: error?.graphQLErrors?.[0]?.message ?? error?.errors?.[0]?.message ?? error?.message ?? String(error) })\n return undefined\n } finally {\n this.busy = false\n }\n }\n\n private input() {\n const { name, counterparty, endCustomer, amount, currency, expectedCloseMonth, stage, probability, ownerId, description } = this.form\n return {\n name,\n counterpartyId: counterparty?.id ?? '',\n endCustomerId: endCustomer?.id ?? null,\n amount: Number(amount || 0),\n currency,\n expectedCloseMonth,\n stage,\n probability: probability === '' ? null : Number(probability),\n ownerId: ownerId || null,\n description\n }\n }\n\n private async save() {\n const id = this.opportunity?.id\n const saved = await this.run(async () => {\n const response = await client.mutate({\n mutation: gql(\n id\n ? `mutation ($id: String!, $opportunity: NewOpportunity!) { opportunity: updateOpportunity(id: $id, opportunity: $opportunity) { id } }`\n : `mutation ($opportunity: NewOpportunity!) { opportunity: createOpportunity(opportunity: $opportunity) { id } }`\n ),\n variables: { ...(id ? { id } : {}), opportunity: this.input() }\n })\n if (response.errors?.length) throw response\n return response.data.opportunity as { id: string }\n })\n if (!saved) return\n if (!id) navigate(`opportunity-editor?id=${saved.id}`)\n else await this.load(id)\n }\n\n private async lose() {\n const id = this.opportunity!.id\n const lost = await this.run(async () => {\n const response = await client.mutate({\n mutation: gql`\n mutation ($id: String!, $reason: String!) {\n loseOpportunity(id: $id, reason: $reason) { id }\n }\n `,\n variables: { id, reason: this.reason.trim() }\n })\n if (response.errors?.length) throw response\n return response.data.loseOpportunity\n })\n if (lost) await this.load(id)\n }\n}\n\nconst eventWord = (action?: string) =>\n action === 'CREATED'\n ? i18next.t('text.opportunity-event-created')\n : action === 'STAGED'\n ? i18next.t('text.opportunity-event-staged')\n : action === 'WON'\n ? i18next.t('text.opportunity-event-won')\n : action === 'LOST'\n ? i18next.t('text.opportunity-event-lost')\n : i18next.t('text.opportunity-event-updated')\n\nfunction blank(stages: { stage: string; probability: number }[] = []): Form {\n const lead = stages.find(one => one.stage === 'LEAD')\n return {\n name: '',\n counterparty: null,\n endCustomer: null,\n amount: '0',\n currency: 'KRW',\n expectedCloseMonth: thisMonth(),\n stage: 'LEAD',\n probability: lead ? String(lead.probability) : '',\n ownerId: '',\n description: ''\n }\n}\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import '@operato/data-grist/ox-grist.js';
|
|
2
|
+
import '@operato/data-grist/ox-filters-form.js';
|
|
3
|
+
import { FetchOption } from '@operato/data-grist';
|
|
4
|
+
import { PageView } from '@operato/shell';
|
|
5
|
+
declare const OpportunityList_base: (new (...args: any[]) => import("lit").LitElement) & typeof PageView;
|
|
6
|
+
/** 영업 기회 — every opportunity of the domain, narrowed by stage. The name opens its 360; the edit gutter its editor. */
|
|
7
|
+
export declare class OpportunityList extends OpportunityList_base {
|
|
8
|
+
static styles: import("lit").CSSResult[];
|
|
9
|
+
gristConfig: any;
|
|
10
|
+
private grist;
|
|
11
|
+
get context(): {
|
|
12
|
+
title: string;
|
|
13
|
+
search: {
|
|
14
|
+
handler: (search: string) => void;
|
|
15
|
+
value: string;
|
|
16
|
+
};
|
|
17
|
+
filter: {
|
|
18
|
+
handler: () => void;
|
|
19
|
+
};
|
|
20
|
+
actions: {
|
|
21
|
+
title: string;
|
|
22
|
+
icon: string;
|
|
23
|
+
emphasis: {
|
|
24
|
+
raised: boolean;
|
|
25
|
+
};
|
|
26
|
+
action: () => void;
|
|
27
|
+
}[];
|
|
28
|
+
exportable: {
|
|
29
|
+
name: string;
|
|
30
|
+
data: () => any[];
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
34
|
+
pageInitialized(): Promise<void>;
|
|
35
|
+
fetchHandler({ page, limit, sortings, filters, search }: FetchOption): Promise<{
|
|
36
|
+
total: any;
|
|
37
|
+
records: any;
|
|
38
|
+
}>;
|
|
39
|
+
}
|
|
40
|
+
export {};
|