Merge branch 'feature/associated-payment-transactions' into develop

This commit is contained in:
elforjani13
2021-12-14 13:06:05 +02:00
27 changed files with 775 additions and 63 deletions

View File

@@ -8,6 +8,7 @@ import { DrawerMainTabs } from 'components';
import BillDetailTab from './BillDetailTab';
import LocatedLandedCostTable from './LocatedLandedCostTable';
import JournalEntriesTable from '../../JournalEntriesTable/JournalEntriesTable';
import BillPaymentTransactionTable from './BillPaymentTransactions/BillPaymentTransactionTable';
import { useBillDrawerContext } from './BillDrawerProvider';
import BillDrawerCls from 'style/components/Drawers/BillDrawer.module.scss';
@@ -33,16 +34,16 @@ export default function BillDrawerDetails() {
id={'journal_entries'}
panel={<JournalEntriesTable transactions={transactions} />}
/>
<Tab
title={intl.get('payment_transactions')}
id={'payment_transactions'}
panel={<BillPaymentTransactionTable />}
/>
<Tab
title={intl.get('located_landed_cost')}
id={'landed_cost'}
panel={<LocatedLandedCostTable />}
/>
{/* <Tab
title={intl.get('payment_transactions')}
id={'payment_transactions'}
// panel={}
/> */}
</DrawerMainTabs>
</div>
);

View File

@@ -5,6 +5,7 @@ import {
useBill,
useTransactionsByReference,
useBillLocatedLandedCost,
useBillPaymentTransactions,
} from 'hooks/query';
const BillDrawerContext = React.createContext();
@@ -33,15 +34,31 @@ function BillDrawerProvider({ billId, ...props }) {
enabled: !!billId,
});
// Handle fetch bill payment transaction.
const {
isLoading: isPaymentTransactionsLoading,
isFetching: isPaymentTransactionFetching,
data: paymentTransactions,
} = useBillPaymentTransactions(billId, {
enabled: !!billId,
});
//provider.
const provider = {
transactions,
billId,
data,
bill,
paymentTransactions,
isPaymentTransactionsLoading,
isPaymentTransactionFetching,
};
const loading = isLandedCostLoading || isTransactionLoading || isBillLoading;
const loading =
isLandedCostLoading ||
isTransactionLoading ||
isPaymentTransactionsLoading ||
isBillLoading;
return (
<DrawerLoading loading={loading}>

View File

@@ -0,0 +1,33 @@
import React from 'react';
import { DataTable, Card } from 'components';
import 'style/pages/PaymentTransactions/List.scss';
import { useBillPaymentTransactionsColumns } from './components';
import { useBillDrawerContext } from '../BillDrawerProvider';
/**
* Bill payment transactions datatable.
*/
export default function BillPaymentTransactionTable() {
const columns = useBillPaymentTransactionsColumns();
const {
paymentTransactions,
isPaymentTransactionsLoading,
isPaymentTransactionFetching,
} = useBillDrawerContext();
return (
<Card>
<DataTable
columns={columns}
data={paymentTransactions}
loading={isPaymentTransactionsLoading}
headerLoading={isPaymentTransactionsLoading}
progressBarLoading={isPaymentTransactionFetching}
className={'payment-transactions'}
/>
</Card>
);
}

View File

@@ -0,0 +1,52 @@
import React from 'react';
import intl from 'react-intl-universal';
import clsx from 'classnames';
import { CLASSES } from '../../../../common/classes';
import { FormatDateCell } from '../../../../components';
/**
* Retrieve bill payment transactions table columns.
*/
export const useBillPaymentTransactionsColumns = () => {
return React.useMemo(
() => [
{
id: 'date',
Header: intl.get('payment_date'),
accessor: 'date',
Cell: FormatDateCell,
width: 110,
className: 'date',
textOverview: true,
},
{
id: 'amount',
Header: intl.get('amount'),
accessor: 'amount',
// accessor: 'formatted_amount',
align: 'right',
width: 100,
className: clsx(CLASSES.FONT_BOLD),
textOverview: true,
},
{
id: 'payment_number',
Header: intl.get('payment_no'),
accessor: 'payment_number',
width: 100,
className: 'payment_number',
},
{
id: 'reference',
Header: intl.get('reference_no'),
accessor: 'reference',
width: 90,
className: 'reference',
clickable: true,
textOverview: true,
},
],
[],
);
};

View File

@@ -7,45 +7,45 @@ import { FormatNumberCell } from '../../../components';
* Retrieve bill readonly details entries table columns.
*/
export const useBillReadonlyEntriesTableColumns = () =>
React.useMemo(
() => [
{
Header: intl.get('product_and_service'),
accessor: 'item.name',
width: 150,
className: 'item',
disableSortBy: true
},
{
Header: intl.get('description'),
accessor: 'description',
className: 'description',
disableSortBy: true
},
{
Header: intl.get('quantity'),
accessor: 'quantity',
Cell: FormatNumberCell,
width: 100,
align: 'right',
disableSortBy: true
},
{
Header: intl.get('rate'),
accessor: 'rate',
Cell: FormatNumberCell,
width: 100,
align: 'right',
disableSortBy: true
},
{
Header: intl.get('amount'),
accessor: 'amount',
Cell: FormatNumberCell,
width: 100,
align: 'right',
disableSortBy: true
},
],
[],
);
React.useMemo(
() => [
{
Header: intl.get('product_and_service'),
accessor: 'item.name',
width: 150,
className: 'item',
disableSortBy: true,
},
{
Header: intl.get('description'),
accessor: 'description',
className: 'description',
disableSortBy: true,
},
{
Header: intl.get('quantity'),
accessor: 'quantity',
Cell: FormatNumberCell,
width: 100,
align: 'right',
disableSortBy: true,
},
{
Header: intl.get('rate'),
accessor: 'rate',
Cell: FormatNumberCell,
width: 100,
align: 'right',
disableSortBy: true,
},
{
Header: intl.get('amount'),
accessor: 'amount',
Cell: FormatNumberCell,
width: 100,
align: 'right',
disableSortBy: true,
},
],
[],
);

View File

@@ -6,6 +6,7 @@ import clsx from 'classnames';
import { DrawerMainTabs } from 'components';
import JournalEntriesTable from '../../JournalEntriesTable/JournalEntriesTable';
import InvoicePaymentTransactionsTable from './InvoicePaymentTransactions/InvoicePaymentTransactionsTable';
import InvoiceDetailTab from './InvoiceDetailTab';
import { useInvoiceDetailDrawerContext } from './InvoiceDetailDrawerProvider';
@@ -30,11 +31,11 @@ export default function InvoiceDetail() {
id={'journal_entries'}
panel={<JournalEntriesTable transactions={transactions} />}
/>
{/* <Tab
<Tab
title={intl.get('payment_transactions')}
id={'payment_transactions'}
// panel={}
/> */}
panel={<InvoicePaymentTransactionsTable />}
/>
</DrawerMainTabs>
</div>
);

View File

@@ -1,7 +1,11 @@
import React from 'react';
import intl from 'react-intl-universal';
import { DrawerHeaderContent, DrawerLoading } from 'components';
import { useTransactionsByReference, useInvoice } from 'hooks/query';
import {
useTransactionsByReference,
useInvoice,
useInvoicePaymentTransactions,
} from 'hooks/query';
const InvoiceDetailDrawerContext = React.createContext();
/**
@@ -25,14 +29,30 @@ function InvoiceDetailDrawerProvider({ invoiceId, ...props }) {
enabled: !!invoiceId,
});
// Fetch invoice payment transactions.
const {
data: paymentTransactions,
isFetching: isPaymentTransactionFetching,
isLoading: isPaymentTransactionLoading,
} = useInvoicePaymentTransactions(invoiceId, {
enabled: !!invoiceId,
});
//provider.
const provider = {
transactions,
paymentTransactions,
isPaymentTransactionLoading,
isPaymentTransactionFetching,
invoiceId,
invoice,
};
return (
<DrawerLoading loading={isTransactionLoading || isInvoiceLoading}>
<DrawerLoading
loading={
isTransactionLoading || isInvoiceLoading || isPaymentTransactionLoading
}
>
<DrawerHeaderContent
name="invoice-detail-drawer"
title={intl.get('invoice_details')}

View File

@@ -0,0 +1,32 @@
import React from 'react';
import { DataTable, Card } from 'components';
import 'style/pages/PaymentTransactions/List.scss';
import { useInvoicePaymentTransactionsColumns } from './components';
import { useInvoiceDetailDrawerContext } from '../InvoiceDetailDrawerProvider';
/**
* Invoice payment transactions datatable.
*/
export default function InvoicePaymentTransactionsTable() {
const columns = useInvoicePaymentTransactionsColumns();
const {
paymentTransactions,
isPaymentTransactionLoading,
isPaymentTransactionFetching,
} = useInvoiceDetailDrawerContext();
return (
<Card>
<DataTable
columns={columns}
data={paymentTransactions}
loading={isPaymentTransactionLoading}
headerLoading={isPaymentTransactionLoading}
progressBarLoading={isPaymentTransactionFetching}
className={'payment-transactions'}
/>
</Card>
);
}

View File

@@ -0,0 +1,52 @@
import React from 'react';
import intl from 'react-intl-universal';
import clsx from 'classnames';
import { CLASSES } from '../../../../common/classes';
import { FormattedMessage as T, FormatDateCell } from '../../../../components';
/**
* Retrieve invoice payment transactions table columns.
*/
export const useInvoicePaymentTransactionsColumns = () => {
return React.useMemo(
() => [
{
id: 'date',
Header: intl.get('payment_date'),
accessor: 'date',
Cell: FormatDateCell,
width: 110,
className: 'date',
textOverview: true,
},
{
id: 'amount',
Header: intl.get('amount'),
accessor: 'amount',
// accessor: 'formatted_amount',
align: 'right',
width: 120,
className: clsx(CLASSES.FONT_BOLD),
textOverview: true,
},
{
id: 'payment_receive_no.',
Header: intl.get('payment_no'),
accessor: 'payment_receive_no',
width: 100,
className: 'payment_receive_no',
},
{
id: 'reference_no',
Header: intl.get('reference_no'),
accessor: 'reference_no',
width: 90,
className: 'reference_no',
clickable: true,
textOverview: true,
},
],
[],
);
};

View File

@@ -105,3 +105,4 @@ export const BadDebtMenuItem = ({
</Popover>
);
};

View File

@@ -2,6 +2,8 @@ import React from 'react';
import ItemDetailActionsBar from './ItemDetailActionsBar';
import ItemDetailHeader from './ItemDetailHeader';
import { ItemPaymentTransactions } from './ItemPaymentTransactions';
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
import { Card } from 'components';
@@ -12,7 +14,7 @@ export default function ItemDetail() {
return (
<div className="item-drawer">
<ItemDetailActionsBar />
<ItemPaymentTransactions />
<Card>
<ItemDetailHeader />
</Card>

View File

@@ -0,0 +1,78 @@
import React from 'react';
import intl from 'react-intl-universal';
import clsx from 'classnames';
import { CLASSES } from '../../../../common/classes';
import { DataTable, Card, FormatDateCell } from '../../../../components';
/**
* Bill payment transactions data table.
*/
export default function BillPaymentTransactions() {
const columns = React.useMemo(
() => [
{
id: 'bill_date',
Header: intl.get('date'),
accessor: 'bill_date',
Cell: FormatDateCell,
width: 110,
className: 'bill_date',
},
{
id: 'bill_number',
Header: intl.get('bill_number'),
accessor: (row) => (row.bill_number ? `${row.bill_number}` : null),
width: 100,
className: 'bill_number',
},
{
id: 'vendor',
Header: intl.get('vendor_name'),
accessor: 'vendor.display_name',
width: 180,
className: 'vendor',
},
{
id: 'reference_no',
Header: intl.get('reference_no'),
accessor: 'reference_no',
width: 90,
className: 'reference_no',
},
{
id: 'qunatity',
Header: 'Quantity Purchase',
},
{
id: 'rate',
Header: 'Rate',
},
{
id: 'total',
Header: intl.get('total'),
},
{
id: 'status',
Header: intl.get('status'),
// accessor: (row) => statusAccessor(row),
width: 160,
className: 'status',
},
],
[],
);
return (
<div className="item-drawer__table">
<Card>
<DataTable
columns={columns}
data={[]}
// loading={}
// headerLoading={}
// progressBarLoading={}
/>
</Card>
</div>
);
}

View File

@@ -0,0 +1,73 @@
import React from 'react';
import intl from 'react-intl-universal';
import clsx from 'classnames';
import { CLASSES } from '../../../../common/classes';
import { DataTable, Card, FormatDateCell } from '../../../../components';
/**
* Esimtate payment transactions datatable.
*/
export default function EstimatePaymentTransactions() {
const columns = React.useMemo(
() => [
{
id: 'estimate_date',
Header: intl.get('date'),
accessor: 'estimate_date',
Cell: FormatDateCell,
width: 110,
className: 'estimate_date',
textOverview: true,
},
{
id: 'estimate_number',
Header: intl.get('estimate_no'),
accessor: 'estimate_number',
width: 100,
className: 'estimate_number',
textOverview: true,
},
{
id: 'reference',
Header: intl.get('reference_no'),
accessor: 'reference',
width: 140,
className: 'reference',
},
{
id: 'customer_id',
Header: intl.get('customer_name'),
accessor: 'customer.display_name',
width: 140,
className: 'customer_id',
},
{
id: 'qunatity',
Header: 'Quantity Sold',
},
{
id: 'rate',
Header: 'Rate',
},
{
id: 'total',
Header: intl.get('total'),
},
],
[],
);
return (
<div className="item-drawer__table">
<Card>
<DataTable
columns={columns}
data={[]}
// loading={}
// headerLoading={}
// progressBarLoading={}
/>
</Card>
</div>
);
}

View File

@@ -0,0 +1,83 @@
import React from 'react';
import intl from 'react-intl-universal';
import clsx from 'classnames';
import { CLASSES } from '../../../../common/classes';
import { DataTable, Card, FormatDateCell } from '../../../../components';
/**
* Invoice payment transactions datatable.
*/
export default function InvoicePaymentTransactionsTable() {
const columns = React.useMemo(
() => [
{
id: 'invoice_date',
Header: intl.get('date'),
accessor: 'invoice_date',
Cell: FormatDateCell,
width: 110,
className: 'invoice_date',
textOverview: true,
},
{
id: 'invoice_no',
Header: intl.get('invoice_no__'),
accessor: 'invoice_no',
width: 240,
className: 'invoice_no',
textOverview: true,
},
{
id: 'customer',
Header: intl.get('customer_name'),
accessor: 'customer.display_name',
width: 140,
className: 'customer_id',
clickable: true,
textOverview: true,
},
{
id: 'reference_no',
Header: intl.get('reference_no'),
accessor: 'reference_no',
width: 140,
className: 'reference_no',
textOverview: true,
},
{
id: 'qunatity',
Header: 'Quantity Sold',
},
{
id: 'rate',
Header: 'Rate',
},
{
id: 'total',
Header: intl.get('total'),
},
{
id: 'status',
Header: intl.get('status'),
// accessor: (row) => statusAccessor(row),
width: 160,
className: 'status',
},
],
[],
);
return (
<div className="item-drawer__table">
<Card>
<DataTable
columns={columns}
data={[]}
// loading={}
// headerLoading={}
// progressBarLoading={}
/>
</Card>
</div>
);
}

View File

@@ -0,0 +1,83 @@
import React from 'react';
import intl from 'react-intl-universal';
import clsx from 'classnames';
import { CLASSES } from '../../../../common/classes';
import { DataTable, Card, FormatDateCell } from '../../../../components';
/**
* Receipt payment transactions datatable.
*/
export default function ReceiptPaymentTransactions() {
const columns = React.useMemo(
() => [
{
id: 'receipt_date',
Header: intl.get('date'),
accessor: 'receipt_date',
Cell: FormatDateCell,
width: 110,
className: 'receipt_date',
textOverview: true,
},
{
id: 'receipt_number',
Header: intl.get('receipt_no'),
accessor: 'receipt_number',
width: 140,
className: 'receipt_number',
clickable: true,
textOverview: true,
},
{
id: 'customer',
Header: intl.get('customer_name'),
accessor: 'customer.display_name',
width: 140,
className: 'customer_id',
textOverview: true,
},
{
id: 'reference_no',
Header: intl.get('reference_no'),
accessor: 'reference_no',
width: 140,
className: 'reference_no',
textOverview: true,
},
{
id: 'status',
Header: intl.get('status'),
// accessor: StatusAccessor,
width: 140,
className: 'status',
},
{
id: 'qunatity',
Header: 'Quantity Sold',
},
{
id: 'rate',
Header: 'Rate',
},
{
id: 'total',
Header: intl.get('total'),
},
],
[],
);
return (
<div className="item-drawer__table">
<Card>
<DataTable
columns={columns}
data={[]}
// loading={}
// headerLoading={}
// progressBarLoading={}
/>
</Card>
</div>
);
}

View File

@@ -0,0 +1,37 @@
import React from 'react';
import { Tab } from '@blueprintjs/core';
import { DrawerMainTabs, FormattedMessage as T } from 'components';
import InvoicePaymentTransactionsTable from './InvoicePaymentTransactionsDataTable';
import EstimatePaymentTransactionsTable from './EstimatePaymentTransactionsDataTable';
import ReceiptPaymentTransactionsTable from './ReceiptPaymentTransactionsDataTable';
import BillPaymentTransactionsTable from './BillPaymentTransactionsDataTable';
import ItemSwitchMenuItem from './utils';
export const ItemPaymentTransactions = () => {
return (
<DrawerMainTabs>
<Tab
id={'invoice'}
title={<T id={'invoice'} />}
panel={<InvoicePaymentTransactionsTable />}
/>
<Tab
id={'estiamte'}
title={'Estimate'}
title={<T id={'estimate_'} />}
panel={<EstimatePaymentTransactionsTable />}
/>
<Tab
id={'receipt'}
title={<T id={'receipt_'} />}
panel={<ReceiptPaymentTransactionsTable />}
/>
<Tab
id={'bill'}
title={'Bill'}
panel={<BillPaymentTransactionsTable />}
/>
</DrawerMainTabs>
);
};

View File

@@ -0,0 +1,59 @@
import React from 'react';
import intl from 'react-intl-universal';
import {
Popover,
Menu,
Position,
Button,
MenuItem,
Classes,
NavbarGroup,
PopoverInteractionKind,
} from '@blueprintjs/core';
import { Icon } from '../../../../components';
import { curry } from 'lodash/fp';
import { Select } from '@blueprintjs/select';
function ItemSwitchMenuItem({ onChange }) {
const Transaction = [
{ name: 'Invoice' },
{ name: 'Estimate' },
{ name: 'Bill' },
{ name: 'Receipt' },
];
const handleSwitchMenutItem = (item) => {
onChange && onChange(item);
};
const content = (
<Menu>
{Transaction.map((item) => (
<MenuItem
onClick={() => handleSwitchMenutItem(item.name)}
text={item.name}
/>
))}
</Menu>
);
return (
<Popover
content={content}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
modifiers={{
offset: { offset: '0, 4' },
}}
minimal={true}
>
<Button
minimal={true}
text="Select Service"
rightIcon={<Icon icon={'arrow-drop-down'} iconSize={24} />}
/>
</Popover>
);
}
export default ItemSwitchMenuItem;

View File

@@ -19,7 +19,12 @@ function ItemDetailDrawer({
payload: { itemId },
}) {
return (
<Drawer isOpen={isOpen} name={name} size={'750px'}>
<Drawer
isOpen={isOpen}
name={name}
style={{ minWidth: '700px', maxWidth: '900px' }}
size={'65%'}
>
<DrawerSuspense>
<ItemDetailDrawerContent itemId={itemId} />
</DrawerSuspense>

View File

@@ -186,3 +186,18 @@ export function useRefreshBills() {
},
};
}
export function useBillPaymentTransactions(id, props) {
return useRequestQuery(
[t.BILLS_PAYMENT_TRANSACTIONS, id],
{
method: 'get',
url: `purchases/bills/${id}/payment-transactions`,
},
{
select: (res) => res.data.data,
defaultData: [],
...props,
},
);
}

View File

@@ -273,3 +273,18 @@ export function useInvoiceSMSDetail(invoiceId, query, props) {
},
);
}
export function useInvoicePaymentTransactions(invoiceId, props) {
return useRequestQuery(
[t.SALE_INVOICE_PAYMENT_TRANSACTIONS, invoiceId],
{
method: 'get',
url: `sales/invoices/${invoiceId}/payment-transactions`,
},
{
select: (res) => res.data.data,
defaultData: [],
...props,
},
);
}

View File

@@ -30,6 +30,9 @@ const commonInvalidateQueries = (client) => {
// Invalidate the cashflow transactions.
client.invalidateQueries(t.CASH_FLOW_TRANSACTIONS);
client.invalidateQueries(t.CASHFLOW_ACCOUNT_TRANSACTIONS_INFINITY);
// Invalidate bills payment transactions.
client.invalidateQueries(t.BILLS_PAYMENT_TRANSACTIONS);
};
/**

View File

@@ -35,6 +35,9 @@ const commonInvalidateQueries = (client) => {
// Invalidate reconcile.
client.invalidateQueries(t.RECONCILE_CREDIT_NOTE);
client.invalidateQueries(t.RECONCILE_CREDIT_NOTES);
// Invalidate invoices payment transactions.
client.invalidateQueries(t.SALE_INVOICE_PAYMENT_TRANSACTIONS);
};
// Transform payment receives.

View File

@@ -30,6 +30,7 @@ const BILLS = {
BILLS: 'BILLS',
BILL: 'BILL',
BILLS_DUE: 'BILLS_DUE',
BILLS_PAYMENT_TRANSACTIONS: 'BILLS_PAYMENT_TRANSACTIONS',
};
const VENDORS = {
@@ -95,6 +96,7 @@ const SALE_INVOICES = {
NOTIFY_SALE_INVOICE_BY_SMS: 'NOTIFY_SALE_INVOICE_BY_SMS',
BAD_DEBT: 'BAD_DEBT',
CANCEL_BAD_DEBT: 'CANCEL_BAD_DEBT',
SALE_INVOICE_PAYMENT_TRANSACTIONS: 'SALE_INVOICE_PAYMENT_TRANSACTIONS',
};
const USERS = {

View File

@@ -1497,5 +1497,6 @@
"users.column.role_name":"دور المستخدم",
"roles.error.you_cannot_edit_predefined_roles":"لا يمكنك تعديل أدوار المستخدم المحددة مسبقا.",
"roles.error.you_cannot_delete_predefined_roles":"لا يمكنك حذف أدوار المستخدم المحددة مسبقا",
"roles.error.you_cannot_delete_role_that_associated_to_users":"لا يمكن حذف دور المستخدم لأنه لديه مستخدمين مرتبطة به."
"roles.error.you_cannot_delete_role_that_associated_to_users":"لا يمكن حذف دور المستخدم لأنه لديه مستخدمين مرتبطة به.",
"payment_transactions":"معاملات الدفع"
}

View File

@@ -1632,6 +1632,6 @@
"transactions_locking.of_the_module_locked_to": "Transactions of the module locked to <strong>{value}</strong>.",
"transactions_locking.lock_reason": "Lock Reason: <strong>{value}</strong>.",
"transactions_locking.partial_unlocked_from": "Partial unlocked from <strong>{fromDate}</strong> to <strong>{toDate}</strong>.",
"transactions_locking.unlock_reason":"<strong>Unlock Reason:</strong> {value}."
"transactions_locking.unlock_reason":"<strong>Unlock Reason:</strong> {value}.",
"payment_transactions":"Payment transactions"
}

View File

@@ -1,12 +1,10 @@
.item-drawer {
.card {
margin: 15px;
padding: 22px 15px;
}
&__content {
.detail-item--name {
width: 30%;
@@ -16,7 +14,6 @@
}
.detail-item--quantity {
.detail-item__content {
font-weight: 600;
@@ -32,7 +29,6 @@
}
.details-menu--horizantal {
.detail-item:not(:first-of-type) {
margin-top: 16px;
}
@@ -42,4 +38,29 @@
}
}
}
}
&__table {
.card {
padding: 12px 15px !important;
}
.table {
.tbody,
.thead {
.tr .th {
padding: 8px 8px;
background-color: #fff;
font-size: 14px;
border-bottom: 1px solid #000;
border-top: 1px solid #000;
}
}
.tbody {
.tr .td {
border-bottom: 0;
padding-top: 0.4rem;
padding-bottom: 0.4rem;
}
}
}
}
}

View File

@@ -0,0 +1,23 @@
.payment-transactions {
padding: 12px;
.table {
.tbody,
.thead {
.tr .th {
padding: 8px 8px;
background-color: #fff;
font-size: 14px;
border-bottom: 1px solid #000;
border-top: 1px solid #000;
}
}
.tbody {
.tr .td {
border-bottom: 0;
padding-top: 0.4rem;
padding-bottom: 0.4rem;
}
}
}
}