mirror of
https://github.com/frappe/books.git
synced 2024-11-13 00:46:28 +00:00
Render ListView based on listConfig
This commit is contained in:
parent
550c2708d2
commit
bd3b266a69
@ -26,9 +26,8 @@ module.exports = {
|
|||||||
fieldtype: 'Link',
|
fieldtype: 'Link',
|
||||||
target: 'Party',
|
target: 'Party',
|
||||||
required: 1,
|
required: 1,
|
||||||
getFilters: (query, control) => {
|
getFilters: (query) => {
|
||||||
return {
|
return {
|
||||||
keywords: ['like', query],
|
|
||||||
customer: 1
|
customer: 1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -38,7 +37,7 @@ module.exports = {
|
|||||||
label: 'Account',
|
label: 'Account',
|
||||||
fieldtype: 'Link',
|
fieldtype: 'Link',
|
||||||
target: 'Account',
|
target: 'Account',
|
||||||
formula: (doc) => doc.getFrom('Party', doc.customer , 'default_account'),
|
formula: (doc) => doc.getFrom('Party', doc.customer , 'defaultAccount'),
|
||||||
getFilters: (query, control) => {
|
getFilters: (query, control) => {
|
||||||
return {
|
return {
|
||||||
keywords: ['like', query],
|
keywords: ['like', query],
|
||||||
@ -145,37 +144,5 @@ module.exports = {
|
|||||||
await form.$formModal.open(payment);
|
await form.$formModal.open(payment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
|
|
||||||
listSettings: {
|
|
||||||
getFields(list) {
|
|
||||||
return ['name', 'customer', 'grandTotal', 'submitted'];
|
|
||||||
},
|
|
||||||
|
|
||||||
getRowHTML(list, data) {
|
|
||||||
return `<div class='col-3'>${list.getNameHTML(data)}</div>
|
|
||||||
<div class='col-4 text-muted'>${data.customer}</div>
|
|
||||||
<div class='col-4 text-muted text-right'>${frappe.format(data.grandTotal, 'Currency')}</div>`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
listView: {
|
|
||||||
columns: [
|
|
||||||
'customer',
|
|
||||||
{
|
|
||||||
label: 'Status',
|
|
||||||
getValue(doc) {
|
|
||||||
return doc.submitted ? 'Paid' : 'Pending';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'grandTotal',
|
|
||||||
'date',
|
|
||||||
{
|
|
||||||
label: 'INV #',
|
|
||||||
getValue(doc) {
|
|
||||||
return doc.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
23
models/doctype/Invoice/InvoiceList.js
Normal file
23
models/doctype/Invoice/InvoiceList.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { _ } from 'frappejs/utils';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
doctype: 'Invoice',
|
||||||
|
title: _('Invoice'),
|
||||||
|
columns: [
|
||||||
|
'customer',
|
||||||
|
{
|
||||||
|
label: 'Status',
|
||||||
|
getValue(doc) {
|
||||||
|
return doc.submitted ? 'Paid' : 'Pending';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'grandTotal',
|
||||||
|
'date',
|
||||||
|
{
|
||||||
|
label: 'INV #',
|
||||||
|
getValue(doc) {
|
||||||
|
return doc.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -80,11 +80,5 @@ module.exports = {
|
|||||||
{ fields: ['tax'] }
|
{ fields: ['tax'] }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
listView: {
|
|
||||||
columns: [
|
|
||||||
'name',
|
|
||||||
'description'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
10
models/doctype/Item/ItemList.js
Normal file
10
models/doctype/Item/ItemList.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { _ } from 'frappejs/utils';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
doctype: 'Item',
|
||||||
|
title: _('Item'),
|
||||||
|
columns: [
|
||||||
|
'name',
|
||||||
|
'description'
|
||||||
|
]
|
||||||
|
}
|
@ -1,13 +1,12 @@
|
|||||||
const BaseList = require('frappejs/client/view/list');
|
import { _ } from 'frappejs/utils';
|
||||||
const frappe = require('frappejs');
|
|
||||||
|
|
||||||
module.exports = class CustomerList extends BaseList {
|
export default {
|
||||||
constructor({doctype, parent, fields, page}) {
|
doctype: 'Party',
|
||||||
super({doctype: 'Party', parent: parent, fields: fields, page: page});
|
title: _('Customer'),
|
||||||
}
|
columns: [
|
||||||
getFilters() {
|
'name'
|
||||||
let filters = super.getFilters();
|
],
|
||||||
filters.customer = 1;
|
filters: {
|
||||||
return filters;
|
customer: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,13 +14,12 @@ module.exports = {
|
|||||||
"required": 1
|
"required": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname: 'default_account',
|
fieldname: 'defaultAccount',
|
||||||
label: 'Default Account',
|
label: 'Default Account',
|
||||||
fieldtype: 'Link',
|
fieldtype: 'Link',
|
||||||
target: 'Account',
|
target: 'Account',
|
||||||
getFilters: (query, control) => {
|
getFilters: (query, control) => {
|
||||||
return {
|
return {
|
||||||
keywords: ['like', query],
|
|
||||||
isGroup: 0,
|
isGroup: 0,
|
||||||
accountType: 'Receivable'
|
accountType: 'Receivable'
|
||||||
};
|
};
|
||||||
@ -48,12 +47,5 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
|
|
||||||
listView: {
|
|
||||||
columns: [
|
|
||||||
'name'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ export default {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Customers',
|
label: 'Customers',
|
||||||
route: '/list/Party'
|
route: '/list/Customer'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Items',
|
label: 'Items',
|
||||||
|
@ -52,30 +52,33 @@ export default {
|
|||||||
return frappe.getMeta(this.doctype);
|
return frappe.getMeta(this.doctype);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created() {
|
created() {
|
||||||
if (!this.name) return;
|
this.loadDoc();
|
||||||
try {
|
|
||||||
this.doc = await frappe.getDoc(this.doctype, this.name);
|
|
||||||
|
|
||||||
if (this.doc._notInserted && this.meta.fields.map(df => df.fieldname).includes('name')) {
|
|
||||||
// For a user editable name field,
|
|
||||||
// it should be unset since it is autogenerated
|
|
||||||
this.doc.set('name', '');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.defaultValues) {
|
|
||||||
for (let fieldname in this.defaultValues) {
|
|
||||||
const value = this.defaultValues[fieldname];
|
|
||||||
this.doc.set(fieldname, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.notFound = true;
|
|
||||||
}
|
|
||||||
this.setLinks();
|
|
||||||
this.doc.on('change', this.setLinks);
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
async loadDoc() {
|
||||||
|
if (!this.name) return;
|
||||||
|
try {
|
||||||
|
this.doc = await frappe.getDoc(this.doctype, this.name);
|
||||||
|
|
||||||
|
if (this.doc._notInserted && this.meta.fields.map(df => df.fieldname).includes('name')) {
|
||||||
|
// For a user editable name field,
|
||||||
|
// it should be unset since it is autogenerated
|
||||||
|
this.doc.set('name', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.defaultValues) {
|
||||||
|
for (let fieldname in this.defaultValues) {
|
||||||
|
const value = this.defaultValues[fieldname];
|
||||||
|
this.doc.set(fieldname, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.notFound = true;
|
||||||
|
}
|
||||||
|
this.setLinks();
|
||||||
|
this.doc.on('change', this.setLinks);
|
||||||
|
},
|
||||||
async save() {
|
async save() {
|
||||||
this.setValidity();
|
this.setValidity();
|
||||||
if (this.isFormInvalid) return;
|
if (this.isFormInvalid) return;
|
||||||
@ -132,8 +135,3 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
|
||||||
.form-container {
|
|
||||||
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -13,23 +13,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
|
import frappe from 'frappejs';
|
||||||
import ListRow from './ListRow';
|
import ListRow from './ListRow';
|
||||||
import ListCell from './ListCell';
|
import ListCell from './ListCell';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'List',
|
name: 'List',
|
||||||
props: ['doctype'],
|
props: ['listConfig'],
|
||||||
watch: {
|
|
||||||
doctype(oldValue, newValue) {
|
|
||||||
if (oldValue !== newValue) {
|
|
||||||
this.setupColumnsAndData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
components: {
|
||||||
ListRow,
|
ListRow,
|
||||||
ListCell
|
ListCell
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
listConfig(oldValue, newValue) {
|
||||||
|
if (oldValue.doctype !== newValue.doctype) {
|
||||||
|
this.setupColumnsAndData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
columns: [],
|
columns: [],
|
||||||
@ -42,6 +43,9 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setupColumnsAndData() {
|
setupColumnsAndData() {
|
||||||
|
this.doctype = this.listConfig.doctype;
|
||||||
|
this.meta = frappe.getMeta(this.doctype);
|
||||||
|
|
||||||
this.prepareColumns();
|
this.prepareColumns();
|
||||||
this.updateData();
|
this.updateData();
|
||||||
},
|
},
|
||||||
@ -49,11 +53,10 @@ export default {
|
|||||||
this.$router.push(`/edit/${this.doctype}/${name}`);
|
this.$router.push(`/edit/${this.doctype}/${name}`);
|
||||||
},
|
},
|
||||||
async updateData(keywords) {
|
async updateData(keywords) {
|
||||||
let filters = null;
|
let filters = this.listConfig.filters || null;
|
||||||
if (keywords) {
|
if (keywords) {
|
||||||
filters = {
|
if (!filters) filters = {};
|
||||||
keywords: ['like', keywords]
|
filters.keywords = ['like', keywords];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.data = await frappe.db.getAll({
|
this.data = await frappe.db.getAll({
|
||||||
doctype: this.doctype,
|
doctype: this.doctype,
|
||||||
@ -62,7 +65,7 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
prepareColumns() {
|
prepareColumns() {
|
||||||
this.columns = this.meta.listView.columns.map(col => {
|
this.columns = this.listConfig.columns.map(col => {
|
||||||
if (typeof col === 'string') {
|
if (typeof col === 'string') {
|
||||||
const field = this.meta.getField(col);
|
const field = this.meta.getField(col);
|
||||||
if (!field) return null;
|
if (!field) return null;
|
||||||
@ -77,11 +80,6 @@ export default {
|
|||||||
return col;
|
return col;
|
||||||
}).filter(Boolean);
|
}).filter(Boolean);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
meta() {
|
|
||||||
return frappe.getMeta(this.doctype);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<search-input class="mr-2" @change="keyword => filterList(keyword)"/>
|
<search-input class="mr-2" @change="keyword => filterList(keyword)"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 d-flex flex-row-reverse">
|
<div class="col-6 d-flex flex-row-reverse">
|
||||||
<f-button primary @click="$emit('newClick')">{{ _('New {0}', doctype) }}</f-button>
|
<f-button primary @click="$emit('newClick')">{{ _('New {0}', listConfig.title) }}</f-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -13,7 +13,7 @@ import SearchInput from '@/components/SearchInput';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ListToolbar',
|
name: 'ListToolbar',
|
||||||
props: ['doctype'],
|
props: ['listConfig'],
|
||||||
components: {
|
components: {
|
||||||
SearchInput
|
SearchInput
|
||||||
},
|
},
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="bg-light">
|
<div class="bg-light">
|
||||||
<page-header :title="meta.label || meta.name" />
|
<page-header :title="listConfig.title" />
|
||||||
<div class="px-4 py-3">
|
<div class="px-4 py-3">
|
||||||
<list-toolbar
|
<list-toolbar
|
||||||
:doctype="doctype"
|
:listConfig="listConfig"
|
||||||
@newClick="openNewForm"
|
@newClick="openNewForm"
|
||||||
@filterList="keyword => filterList(keyword)"
|
@filterList="keyword => filterList(keyword)"
|
||||||
class="mb-4"
|
class="mb-4"
|
||||||
/>
|
/>
|
||||||
<list
|
<list
|
||||||
:doctype="doctype"
|
:listConfig="listConfig"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -20,27 +20,35 @@ import Observable from 'frappejs/utils/observable';
|
|||||||
import PageHeader from '@/components/PageHeader';
|
import PageHeader from '@/components/PageHeader';
|
||||||
import ListToolbar from './ListToolbar';
|
import ListToolbar from './ListToolbar';
|
||||||
import List from './List';
|
import List from './List';
|
||||||
|
import listConfigs from './listConfig';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ListView',
|
name: 'ListView',
|
||||||
props: ['doctype'],
|
props: ['listName'],
|
||||||
components: {
|
components: {
|
||||||
PageHeader,
|
PageHeader,
|
||||||
ListToolbar,
|
ListToolbar,
|
||||||
List
|
List
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
meta() {
|
|
||||||
return frappe.getMeta(this.doctype);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
created() {
|
||||||
frappe.listView = new Observable();
|
frappe.listView = new Observable();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async openNewForm() {
|
async openNewForm() {
|
||||||
const doc = await frappe.getNewDoc(this.doctype);
|
const doctype = this.listConfig.doctype;
|
||||||
this.$router.push(`/edit/${this.doctype}/${doc.name}`);
|
const doc = await frappe.getNewDoc(doctype);
|
||||||
|
if (this.listConfig.filters) {
|
||||||
|
doc.set(this.listConfig.filters);
|
||||||
|
}
|
||||||
|
this.$router.push(`/edit/${doctype}/${doc.name}`);
|
||||||
|
doc.on('afterInsert', () => {
|
||||||
|
this.$router.push(`/edit/${doctype}/${doc.name}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
listConfig() {
|
||||||
|
return listConfigs[this.listName];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
src/pages/ListView/listConfig.js
Normal file
9
src/pages/ListView/listConfig.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import Invoice from '../../../models/doctype/Invoice/InvoiceList';
|
||||||
|
import Customer from '../../../models/doctype/Party/CustomerList';
|
||||||
|
import Item from '../../../models/doctype/Item/ItemList';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
Invoice,
|
||||||
|
Customer,
|
||||||
|
Item
|
||||||
|
}
|
@ -14,7 +14,7 @@ Vue.use(Router);
|
|||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: '/list/:doctype',
|
path: '/list/:listName',
|
||||||
name: 'ListView',
|
name: 'ListView',
|
||||||
component: ListView,
|
component: ListView,
|
||||||
props: true
|
props: true
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
@import "variables.scss";
|
@import "variables.scss";
|
||||||
@import "~bootstrap/scss/bootstrap";
|
@import "~bootstrap/scss/bootstrap";
|
||||||
|
@import "~frappe-datatable/dist/frappe-datatable.css";
|
||||||
|
|
||||||
html {
|
html {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
Loading…
Reference in New Issue
Block a user