sort/app/(tabs)/order.tsx

816 lines
25 KiB
TypeScript
Raw Normal View History

2024-09-07 01:22:11 +00:00
import React, { useEffect, useState } from 'react';
import { StyleSheet, View, Text, Dimensions, TextInput, FlatList, TouchableOpacity, Alert, BackHandler, Modal, ImageBackground, Image } from 'react-native';
import { Picker } from '@react-native-picker/picker';
import { RootStackParamList } from '@/types';
import { useNavigation, NavigationProp, useFocusEffect } from '@react-navigation/native';
import { currency } from '@/components/Helper';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { cfg, generateUUID } from '@/components/lib/cfg';
import { DB } from '@/components/lib/db';
import CustomPicker from '@/components/native/dropdown';
import { Ionicons } from '@expo/vector-icons'; // Optional for adding icons
import config from '../../components/data/config.json';
import InvoiceScreen from '@/components/pageComponent/invoice';
import TimeLive from '@/components/Time';
var width = Dimensions.get('window').width;
var height = Dimensions.get('window').height;
const ProductCard = ({ item, onSlopChange, onBalChange, onBoxChange }: {
item: { id: number; name: string; price: number; slop: number; bal: number; box: number };
onSlopChange: (id: number, value: string) => void;
onBalChange: (id: number, value: string) => void;
onBoxChange: (id: number, value: string) => void;
}) => {
return (
<View style={styles.productCard}>
<View style={styles.productRow}>
<View style={styles.productInfo}>
<Text style={styles.productName}>{item.name}</Text>
</View>
<View style={{ alignItems: 'flex-end' }}>
<Text style={styles.productPrice}>Rp{currency(item.price*10, 0)}</Text>
</View>
</View>
<View style={styles.productRow}>
<View>
<View style={styles.satuan}>
<Text style={[styles.textSatuan,{ color: '#fff' }]}>DOS</Text>
</View>
<TextInput
style={styles.input}
keyboardType="numeric"
value={String(item.box)}
onChangeText={(text) => onBoxChange(item.id, text)}
/>
</View>
<View>
<View style={styles.satuan}>
<Text style={[styles.textSatuan,{ color: '#fff' }]}>BAL</Text>
</View>
<TextInput
style={styles.input}
keyboardType="numeric"
value={String(item.bal)}
onChangeText={(text) => onBalChange(item.id, text)}
/>
</View>
<View>
<View style={styles.satuan}>
<Text style={[styles.textSatuan,{ color: '#fff' }]}>SLOP</Text>
</View>
<TextInput
style={styles.input}
keyboardType="numeric"
value={String(item.slop)}
onChangeText={(text) => onSlopChange(item.id, text)}
/>
</View>
</View>
</View>
);
};
const App = () => {
const [employeeData, setEmployeeData] = useState({ name: '', work_email: '', work_phone: '' });
const [products, setProducts] = useState<any>([]);
const [alamat,SetAlamat] = useState('-');
const [loaderStatus, setLoaderStatus] = useState(false);
const [salesName, setSalesName] = useState('-')
const [sendData, setSendData] = useState<any>({ invoice: {}, dataPilihan :{}})
const [invoice , setInvoice] = useState(false)
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
useFocusEffect(
React.useCallback(() => {
const onBackPress = () => {
console.log(invoice)
if (invoice == true) {
setInvoice(false);
} else {
navigation.navigate('home')
}
return true; // Prevent default behavior (exit app)
};
BackHandler.addEventListener('hardwareBackPress', onBackPress);
navigation.setOptions({ headerLeft: () => null }); // Remove back button
return () => {
BackHandler.removeEventListener('hardwareBackPress', onBackPress);
};
}, [navigation,invoice, setInvoice])
);
const ToInsert = function (array: any, table: any, wht: any) {
array = array ? array : [];
wht = wht ? wht : [];
table = table ? table : "example";
let s = array;
if (s.length > 0) {
var y = Object.keys(s[0]);
var x = '';
x += 'INSERT INTO ';
x += table;
x += '(';
x += y.map(function (u) {
return ` ${u} `
}).join(',');
x += ')';
x += '\n';
x += 'SELECT ';
x += y.map(function (g) {
return `a.${g}`;
});
x += ' FROM (';
x += s.map(function (w: any) {
var f = ` SELECT `;
f += y.map(function (q) {
if (w[q] != null) {
if (typeof w[q] === 'boolean') {
return `${w[q]} AS ${q}`;
}
if (typeof w[q] === 'number') {
return `${w[q]} AS ${q}`;
}
if (typeof w[q] === 'object' && w[q] == null) {
return `null AS ${q}`;
}
if (w[q] == 'timestamp') {
return `now() AS ${q}`;
}
return `'${w[q].toString().replace(/\"/g, "\\\"")}' AS ${q}`;
}
else {
return `'-' AS ${q}`;
}
}).join(",");
return f;
}).join("\n UNION ALL \n")
x += ') a';
if (Array.isArray(wht) && wht.length > 0) {
x += ` LEFT JOIN ${table} ON `
x += wht.map(function (whtx) {
return ` ${table}.${whtx} = a.${whtx} `;
}).join(" AND ");
x += ` WHERE `;
x += wht.map(function (whtx) {
return ` ${table}.${whtx} IS NULL `;
}).join(" AND ");
}
return x;
} else {
return [];
}
};
useEffect(()=>{
(async function(){
let data : any = cfg.dataPilihan.product;
let y = data.map((d:any, i:any)=>{
return {
id: d.id,
name: d.name,
sku:'-',
price: Number(d.list_price),
slop:0,
bal:0,
box:0
}
});
2024-09-24 00:17:23 +00:00
setProducts(y);
2024-09-07 01:22:11 +00:00
})();
},[])
const [isChanged, setIsChanged] = useState(false);
const handleSlopChange = (id:any, value:any) => {
setProducts(
products.map((product:any) =>
product.id === id ? { ...product, slop: parseInt(value) || 0 } : product
)
);
setIsChanged(true);
};
const handleBalChange = (id:any, value:any) => {
setProducts(
products.map((product:any) =>
product.id === id ? { ...product, bal: parseInt(value) || 0 } : product
)
);
setIsChanged(true);
};
const handleBoxChange = (id:any, value:any) => {
setProducts(
products.map((product:any) =>
product.id === id ? { ...product, box: parseInt(value) || 0 } : product
)
);
setIsChanged(true);
};
const calculateTotal = () => {
return products.reduce((total:any, product:any) => {
return total + ((product.slop*10) + (product.bal*100) + (product.box*500)) * product.price;
}, 0);
};
const renderProductItem = (props:any) => (
<ProductCard
item={props?.item}
onSlopChange={handleSlopChange}
onBalChange={handleBalChange}
onBoxChange={handleBoxChange}
/>
);
const [selectedValue, setSelectedValue] = useState('');
const options = cfg.dataPilihan.customer.length > 0 ? [{ label: "Pilih Customer", value:""}].concat(cfg.dataPilihan.customer.map((data:any, i:number)=>{
return { label: data.name, value: typeof data.id === 'number' ? data.id.toString(): data.id };
})) : [
{ label: 'CAHAYA UTAMA', value: 'CAHAYA UTAMA' },
{ label: 'MERTASARI', value: 'MERTASARI' },
{ label: 'PRIMA JAYA', value: 'PRIMA JAYA' },
{ label: 'ARIS Dalung', value: 'ARIS Dalung' },
{ label: 'JAYA KERTI Ubung', value: 'JAYA KERTI Ubung' },
{ label: 'JAYA KERTI Gatsu', value: 'JAYA KERTI Gatsu' },
{ label: 'AGUS WISNU', value: 'AGUS WISNU' },
{ label: 'SINAR WANGI', value: 'SINAR WANGI' },
{ label: 'CRYSTAL', value: 'CRYSTAL' },
];
useEffect(() => {
getEmployeeData();
}, []);
const getEmployeeData = async () => {
try {
const jsonValue = await AsyncStorage.getItem('login');
if (jsonValue !== null) {
const data = JSON.parse(jsonValue);
const sales = data[0];
setEmployeeData(data[0]);
setSalesName(sales.name.toUpperCase())
}
} catch (e) {
console.error(e);
}
};
const mapTax = function(){
}
const mapsDataOrder = function(data:any){
cfg.invoice.token_access = generateUUID()
return [{
company_id: data.company_id ? data.company_id : 1,
partner_id: data.partner_id ? data.partner_id : null,
partner_invoice_id: data.partner_id ? data.partner_id : null,
partner_shipping_id: data.partner_id ? data.partner_id : null,
user_id : data.user_id ? data.user_id : null ,
team_id : data.team_id ? data.team_id : 1 ,
create_uid: data.create_uid ? data.create_uid : null,
write_uid : data.create_uid ? data.create_uid : 1 ,
name : data.name ? data.name : null ,
state: 'draft' ,
access_token: cfg.invoice.token_access,
client_order_ref: data.email ? data.email :null ,
invoice_status: data.invoice_status ? data.invoice_status :"no" ,
validity_date: data.validity_date ? data.validity_date :"timestamp" ,
currency_id: data.currency_id ? data.currency_id :12 ,
currency_rate: data.currency_rate ? data.currency_rate :1.0 ,
amount_untaxed: data.amount_untaxed ? data.amount_untaxed :null ,
amount_tax: data.amount_tax ? data.amount_tax :null ,
amount_total: data.amount_total ? data.amount_total :null ,
amount_to_invoice: data.amount_to_invoice ? data.amount_to_invoice :null ,
locked: data.locked ? data.locked :false ,
require_signature: data.require_signature ? data.require_signature :true ,
require_payment: data.require_payment ? data.require_payment :false ,
create_date: data.create_date ? data.require_payment :"timestamp" ,
date_order: data.date_order ? data.date_order :"timestamp" ,
write_date: data.write_date ? data.write_date :"timestamp" ,
prepayment_percent: data.prepayment_percent ? data.prepayment_percent :1 ,
warehouse_id: data.warehouse_id ? data.warehouse_id :1 ,
picking_policy: data.picking_policy ? data.picking_policy :"direct" ,
x_studio_sales_man: data.x_studio_sales_man ? data.x_studio_sales_man : null
}];
}
const mapsDataOrderLine = function(data:any){
return Array.isArray(data) ? data.map((d,i)=>{
return {
order_id: d.order_id? d.order_id : null,
sequence : d.sequence ? d.sequence : 10,
company_id: d.company_id ? d.company_id : 1,
currency_id: d.currency_id ? d.currency_id : 12,
order_partner_id: d.order_partner_id ? d.order_partner_id : null,
salesman_id: d.salesman_id ? d.salesman_id : 2,
product_id: d.product_id ? d.product_id : null,
product_uom: d.product_uom ? d.product_uom : 27,
create_uid: d.create_uid ? d.create_uid : null,
write_uid: d.write_uid ? d.write_uid : null,
state: d.state ? d.state : "sale",
qty_delivered_method: d.qty_delivered_method ? d.qty_delivered_method : "stock_move",
invoice_status: d.invoice_status ? d.invoice_status : "no",
name: d.name ? d.name : null,
product_uom_qty: d.product_uom_qty ? d.product_uom_qty : 0,
price_unit: d.price_unit ? d.price_unit : 0,
discount: d.discount ? d.discount : 0,
price_subtotal: d.price_subtotal ? d.price_subtotal : 0,
price_total: d.price_total ? d.price_total : 0,
price_reduce_taxexcl: d.price_reduce_taxexcl ? d.price_reduce_taxexcl : 0,
price_reduce_taxinc: d.price_reduce_taxinc ? d.price_reduce_taxinc : 0,
qty_delivered: d.qty_delivered ? d.qty_delivered : 0,
qty_invoiced: d.qty_invoiced ? d.qty_invoiced : 0,
qty_to_invoice: d.qty_to_invoice ? d.qty_to_invoice : 0,
untaxed_amount_invoiced: d.untaxed_amount_invoiced ? d.untaxed_amount_invoiced : 0,
untaxed_amount_to_invoice: d.untaxed_amount_to_invoice ? d.untaxed_amount_to_invoice : 0,
is_downpayment: d.is_downpayment ? d.is_downpayment : false,
create_date: d.create_date ? d.create_date : 'timestamp',
write_date: d.write_date ? d.write_date : 'timestamp',
price_tax: d.price_tax ? d.price_tax : 0,
product_packaging_qty: d.product_packaging_qty ? d.product_pakaging_qty : 0,
customer_lead: d.customer_lead ? d.customer_lead : 0,
is_service: d.is_service ? d.is_service : false,
planning_hours_planned: d.planning_hours_planned ? d.planning_hours_planned : 0,
planning_hours_to_plan: d.planning_hours_to_plan ? d.planning_hours_to_plan : 0,
}
}):[];
}
function getFormattedDate() {
const today = new Date();
const day = String(today.getDate()).padStart(2, '0');
const month = String(today.getMonth() + 1).padStart(2, '0'); // Months are zero-based
const year = today.getFullYear();
return `${day}/${month}/${year}`;
}
const saveAction = async ()=>{
const activeTax = 1;
const [tax] = cfg.dataPilihan.tax.filter((s:any)=> s.id == activeTax );
const taxValue = Number(tax?.amount) / 100;
const sales: any = await AsyncStorage.getItem('login')
const [salesman]: any = JSON.parse(sales);
let [cus] = cfg.dataPilihan.customer.filter((data:any)=> data.id == selectedValue );
if(!cus){
Alert.alert("Warning", "silahkan pilih customer terlebih dahulu!");
return true;
}
cus = cus ? cus:null;
let totalHarga = 0;
let amount_untaxed = 0;
let amount_tax = 0;
let amount_total = 0;
let amount_to_invoice = 0;
// fungsi penghiungan tax
const taxCount = function(){
return {
nilaiTax : (taxValue * totalHarga),
nilaiTotalAndTax : ((1+taxValue) * totalHarga),
nilaiTotal : totalHarga
}
}
const perbandinganHarga = {
pcs : 1,
slop : 10,
bal : 100,
box : 500
}
let getProduk = products.filter((s:any)=>{
if(s.box != 0 || s.bal != 0 || s.slop != 0 ){
return s;
}
});
products.forEach((n: any, i: any) => {
let box = n.box * perbandinganHarga.box * n.price;
let bal = n.bal * perbandinganHarga.bal * n.price;
let slop = n.slop * perbandinganHarga.slop * n.price;
totalHarga += (box + bal + slop);
});
if (totalHarga === 0) {
Alert.alert("Warning", "silahkan isi total pesanan terlebih dahulu!");
return true;
}
setLoaderStatus(true)
const [newName]: any = await DB(`
SELECT 'S' || LPAD((name + 1)::text, 5, '0') as name
FROM (
SELECT split_part(name, 'S', -1)::integer AS name
FROM sale_order
ORDER BY name DESC
LIMIT 1
) a;
`);
const name = newName.name;
// default user_id menggunakan administrator
const user_id = 2;
// default company_id administrator
const company_id = 1;
let dataNilai = taxCount();
amount_untaxed = dataNilai.nilaiTotal
amount_tax = dataNilai.nilaiTax;
amount_total = dataNilai.nilaiTotalAndTax;
try{
(async function SimpanData(){
let dataOrder = mapsDataOrder({
user_id: user_id,
company_id: company_id,
partner_id: cus ? cus.id : 0,
create_uid: salesman ? salesman.id : null,
email: salesman ? salesman.work_email : '',
amount_untaxed: amount_untaxed,
amount_tax: amount_tax,
amount_total: amount_total,
amount_to_invoice: amount_total,
name: name,
x_studio_sales_man: salesman?.id
});
cfg.invoice.sales_order = dataOrder;
cfg.invoice.times = getFormattedDate();
setSendData((function (d: any) {
d.invoice.sales_order = cfg.invoice.sales_order;
d.invoice.times = cfg.invoice.times;
d.dataPilihan = cfg.dataPilihan;
return d;
})(sendData));
let data: any = await DB(
ToInsert(dataOrder, "sale_order", [])
);
let ds: any = await DB(`SELECT id as newId FROM sale_order ORDER BY id DESC LIMIT 1`);
2024-09-24 00:17:23 +00:00
2024-09-07 01:22:11 +00:00
let newProduct:any = [];
2024-09-24 00:17:23 +00:00
2024-09-07 01:22:11 +00:00
getProduk.forEach((c:any)=>{
// dos
if(c.box > 0){
newProduct.push({
id: c.id,
name: c.name,
price: c.price * 500,
product_uom: 30,
qty : c.box,
sku : c.sku
});
}
// bal
if(c.bal > 0){
newProduct.push({
id: c.id,
name: c.name,
price: c.price * 100,
product_uom: 29,
qty : c.bal,
sku : c.sku
});
}
// slop
if(c.slop > 0){
newProduct.push({
id: c.id,
name: c.name,
price: c.price * 10,
product_uom: 28,
qty : c.slop,
sku : c.sku
});
}
});
let dataItem: any = mapsDataOrderLine(newProduct.map((s: any) => {
return {
company_id: company_id,
product_id: s.id,
salesman_id : salesman.id,
order_partner_id: cus.id,
create_uid: salesman.id,
write_uid: salesman.id,
name : s.name,
product_uom: s.product_uom,
product_uom_qty : s.qty,
price_unit: s.price,
price_subtotal: s.qty * s.price,
price_total: (s.qty * s.price) * (1 + taxValue),
price_reduce_taxexcl: s.price,
price_reduce_taxinc: s.price * (taxValue),
price_tax: s.price * (1+taxValue),
order_id: Number(ds[0].newid)
}
}));
cfg.invoice.sales_order_line = dataItem;
setSendData((function(d:any){
d.invoice.sales_order_line = dataItem;
return d;
})(sendData));
// console.log("Produk :", ToInsert(dataItem, "sale_order_line",[]));
let data2:any = await DB(
ToInsert(dataItem, "sale_order_line", [])
);
let saveTax:any = await DB(`SELECT * FROM sale_order_line WHERE order_id = ${ds[0].newid}`)
let saveTaxItem = saveTax.map(function(item:any){
return {
sale_order_line_id : item.id,
account_tax_id : 1
}
});
let data3: any = await DB(
ToInsert(saveTaxItem, "account_tax_sale_order_line_rel", [])
);
console.log("Order :", "----------------------------------------");
setLoaderStatus(false)
clearForm()
setInvoice(true);
})();
}catch(e){
console.log(e);
setLoaderStatus(false)
}
}
const clearForm = () => {
setSelectedValue('')
setProducts(
products.map((product: any) => ({ ...product, slop: 0, bal: 0, box: 0 }))
);
}
const callNumber = () => {
console.log("call")
}
let c:any = 1;
return (
<>
{
invoice?<>
<InvoiceScreen cfg={sendData} navigation={navigation} />
</>:<>
{loaderStatus === true ? <>
<View style={styles.loader}>
<Image
source={require('../../assets/images/loader/load.gif')}
style={{ width: 120, height: 120 }}
resizeMode="contain" // You can adjust this based on how you want the image to scale
/>
</View>
</>:<></>}
<View style={styles.container}>
<ImageBackground
source={require('../../assets/images/bg/SORT_bg_Order.png')}
style={{ justifyContent: 'flex-start', top: 0, height:300 }}
>
<View style={{padding: 20}}>
<View style={styles.informationContainer}>
<View style={{
flexDirection:"row",
width:width-80
}}>
<View style={{flex:1}}>
<Text style={styles.label}>SALES</Text>
<Text style={styles.name}>{salesName}</Text>
</View>
<View>
<TimeLive/>
</View>
</View>
<Text style={styles.label}>CUSTOMER</Text>
<View style={{ marginBottom:10, flexDirection: "row", width: width - 60 }}>
<View style={{ width: width - 120 }}>
<CustomPicker
options={options}
selectedValue={selectedValue}
onValueChange={setSelectedValue}
/>
</View>
<View style={{flex:1, justifyContent:'center', alignItems:'flex-end'}}>
<TouchableOpacity onPress={callNumber}>
<Ionicons
name="logo-whatsapp"
size={30}
color={"white"}
style={{ marginRight: 15 }}
/>
</TouchableOpacity>
</View>
</View>
<Text style={styles.label}>ALAMAT</Text>
{selectedValue && selectedValue.toLocaleLowerCase() != 'pilih customer' ? <>
<Text style={styles.alamat}>{(function(data:any){
if(Array.isArray(data)){
let [d] = data;
return d.street ? d.street:'-';
}
return '-'
})(cfg.dataPilihan.customer.filter((pop: any) => pop.id == selectedValue)) }</Text>
</> : <>
<Text style={styles.alamat}>-</Text>
</>}
</View>
</View>
</ImageBackground>
<FlatList
data={products}
style={styles.productList}
renderItem={renderProductItem}
keyExtractor={(item) => item.id.toString()}
contentContainerStyle={{ paddingHorizontal: 16, paddingBottom: 100 }}
/>
<View style={styles.bottomContainer}>
<View>
<Text style={styles.totalText}>Total:</Text>
<Text style={styles.totalPrice}>Rp{currency(calculateTotal(), 0)}</Text>
</View>
<TouchableOpacity style={styles.continueButton} onPress={() => {
saveAction()
}}>
<Text style={styles.continueButtonText}>Simpan</Text>
</TouchableOpacity>
</View>
</View>
</>
}
</>
);
};
const styles = StyleSheet.create({
loader:{
position:'absolute',
zIndex:1,
height:'100%',
width:'100%',
backgroundColor: config.color.primary,
flexDirection :'row',
justifyContent:'center',
alignItems:'center'
},
container: {
position:'relative',
flex: 1,
backgroundColor: '#f0f0f0',
paddingTop: 0,
overflow: "hidden"
},
alamat:{
fontSize: 14,
color:'white'
},
header: {
position: "absolute",
top: 0,
left: 0,
height: 200,
width: "100%",
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 16,
},
informationContainer: {
width: width,
height: 200,
marginLeft: 16,
marginTop: 16,
},
name: {
fontSize: 20,
marginBottom: 5,
fontWeight: 'bold',
color: '#ffffff',
},
label: {
fontSize: 12,
color: '#ffffff',
marginBottom: 5,
marginTop: 10,
fontWeight:'700'
},
productList: {
flex: 1,
paddingTop: 5,
},
productCard: {
backgroundColor: '#fff',
borderRadius: 8,
shadowColor: '#000',
shadowOpacity: 0.2,
shadowOffset: { width: 0, height: 2 },
shadowRadius: 4,
padding: 16,
marginBottom: 16,
},
productRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
productInfo: {
flex: 1,
marginRight: 16,
},
productName: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 4,
},
productPrice: {
fontSize: 16,
fontWeight: 'bold',
color: 'red',
},
textSatuan: {
fontSize: 16,
fontWeight:'600'
},
satuan: {
fontWeight: 'bold',
height: 40,
width: 75,
backgroundColor: '#2563eb',
borderRadius: 4,
justifyContent: 'center',
alignItems: 'center',
marginTop: 10,
marginBottom: 5,
paddingVertical: 5
},
input: {
height: 40,
borderColor: '#ccc',
borderWidth: 1,
borderRadius: 8,
paddingHorizontal: 10,
marginTop: 10,
marginBottom: 10,
textAlign: 'center',
},
bottomContainer: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
padding: 15,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor: '#2563eb',
},
totalText: {
fontSize: 14,
fontWeight: 'bold',
color: '#fff',
},
totalPrice: {
fontSize: 24,
fontWeight: 'bold',
color: '#fff',
},
continueButton: {
backgroundColor: '#93C854',
borderRadius: 8,
paddingVertical: 10,
paddingHorizontal: 20,
alignItems: 'center',
},
continueButtonText: {
color: '#fff',
fontSize: 18,
fontWeight: 'bold',
},
});
export default App;